DCFFVII Research Thread

cold_spirit

he/him
AKA
Alex T
Finally sat down (with a beverage!) to watch your Chapter 10 videos.

Your skills and analysis are exquisite. Even though I know you're gonna get the S rank, I'm still on the edge of my seat as I watch. The kiillchain set ups, using Vincent's melee to stun enemies, quick menuing, enemy position memorization, version difference commentary, I've got nothing but respect for what you do. You even brought honor to the family by defeating all 100 enemies.

My only issue is that I can't play Dirge this instant!
 
Thank you so much for your post @cold_spirit! It lifts my heart. ^__^

My hope is that I will inspire somebody to record an All S Ranks run in the International version of Dirge. Once I'm done with my run in the Japanese original I have no plans to redo this challenge in any other version of the game. I'd much rather speedrun the Extra Missions instead.

...If I WERE to do a post-JORG version of this challenge though, I imagine my setup would be like this.

Difficulty Mode
- Ex Hard​
Playstyle and arbitrary limitations
- Mouse and keyboard. No PS2 controller allowed.​
- Manual scope all the way. Auto-lock is not allowed.​
- Vincent always on Lv1.​
- No Phoenix Down (auto-life) status.​
- No Manaheart/Manamind/Manasoul until the second shop in Chapter 6. (Maybe even banning these items outright?!)​
Graphics and performance
- XRGB-Mini Framemeister (or equivalent) for upscaling to 1080p​
- HDD-based loading with use of Open PS2 Loader​


That'd be cool, huh? You know what would be even cooler? If somebody who wasn't me tried this challenge. :monster:
 
Last edited:
McDirge version 0.3
Save editor for Dirge of Cerberus: Final Fantasy VII

- Command-line based program
- Language: C++
- Developer Environment: Code::Blocks 17.12
- Compatibility only tested on my Windows 8.1 PC

Click on attached file at the bottom of this post in order to download the executable. Save files for DCFFVII International included in the package so you can play around.

mcdirgever0.3_demopic.png

Still very limited in terms of the shortcuts available for the user to easily edit but this is a good start considering that I've never written a program before that is designed for not just my own use.



Prerequisites for using McDirge:

- Free McBoot, with at least (u)LaunchELF, on physical PS2 memory card
- Dirge of Cerberus save on physical PS2 memory card
- PS2 console
- USB flash drive
- Computer to run McDirge_v0.3.exe on

INSTRUCTIONS
Place any of the following files in the same folder as McDirge_v0.3.exe in order to use the program.
The files can be acquired by copying them from your save data with the use of (u)LaunchELF.

Code:
FILE        MISCELLANEOUS INFO
    ----        ------------------
    BISLPM-66271000 [NTSC-J, Tempsave data] Japanese Original
    BISLPM-66271001 [NTSC-J] Japanese Original

    BASLUS-21419000 [NTSC-US, Tempsave data]
    BASLUS-21419001 [NTSC-US]

    BESLES-54185000 [PAL, Tempsave data]
    BESLES-54185001 [PAL]

    BISLPM-66629000 [NTSC-J, Tempsave data] DCFFVII International
    BISLPM-66629001 [NTSC-J] DCFFVII International

    cfg000      [Configuration File: Basic Settings & Extra Features]
    scene000    [Checkpoint data]
    scene001    [Checkpoint data]
    scene002    [Checkpoint data]
    scene003    [Checkpoint data]
    scene004    [Checkpoint data]
    scene005    [Checkpoint data]
    scene006    [Checkpoint data]
    scene007    [Checkpoint data]
    scene008    [Checkpoint data]
    scene009    [Checkpoint data]
    scene010    [Checkpoint data]
    scr000      [Tempsave Screenshot]

The files "icon.sys" and "kel.ico" deal with the 3D icon for Cerberus save data.
These are not encrypted and McDirge is not built to edit these.

After making the edits to a given file, simply paste the files back into the save folder
with the use of (u)LaunchELF. Boot up the game and all the edits should be in effect.


ABOUT READING AND EDITING cfg000 (user friendly interface)
- If you've begun a new Dirge save file but haven't made any edits to "Control Settings" or "Other Settings", the file will be untouched and
consist almost entirely of values of 0.

In gameplay the default settings will be active, but this is not reflected in any values performed if you read cfg000 with McDirge at this point. Simply put, cfg000 is not "formatted" yet, at this stage. This is true also for Controller Setup, Keyboard Setup and Mouse Setup.

Changing even a single thing, like the vibration, to Off instead of On will result in cfg000 being formatted.


- If you make a Tempsave available when there is no actual tempsave data, then a placeholder Tempsave will be created that simply leads you to the start of the new game. This also reveals the unused placeholder image with Cait Sith in the checkpoints menu.

TsYxB7N.png


Source code:
Code:
///MCDIRGE: VERSION 0.3, COMMAND LINE EDITION
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;


///GLOBAL VARIABLES

///DECLARING THE KEY BLOCKS IN GLOBAL SPACE TO BE AVAILABLE EVERYWHERE
uint8_t KeyBlocksArray[32][8] =
{
{0x45, 0x48, 0x79, 0xEC, 0x35, 0x82, 0x1B, 0xC6},
{0x59, 0x69, 0x5E, 0x9E, 0x0D, 0x8B, 0x89, 0xDE},
{0xBD, 0x0E, 0xD8, 0x17, 0x44, 0xB7, 0xAF, 0x58},
{0xB1, 0x49, 0x38, 0x77, 0x54, 0x94, 0x6E, 0xBB},
{0x75, 0x70, 0x19, 0x54, 0xA6, 0xE5, 0x28, 0xA9},
{0x49, 0x32, 0x7F, 0xA4, 0x3F, 0x7C, 0xCC, 0x4D},
{0x6D, 0xFB, 0x7B, 0x36, 0x3E, 0x6D, 0xFE, 0x84},
{0x21, 0xE9, 0x6B, 0x10, 0x37, 0x22, 0xF8, 0x98},
{0xA5, 0x8D, 0x1B, 0x52, 0x13, 0xAB, 0xD8, 0xFC},
{0x39, 0xC4, 0x89, 0x9A, 0x60, 0x57, 0x3B, 0xF0},
{0x1D, 0xD5, 0xB0, 0x04, 0xE3, 0xB4, 0x28, 0xB1},
{0x91, 0x29, 0x74, 0x17, 0x6F, 0x88, 0xCB, 0x75},
{0xD5, 0xCF, 0x44, 0x75, 0x2B, 0xAA, 0xF9, 0x4C},
{0x29, 0x0F, 0x58, 0x4A, 0xD9, 0x52, 0xE0, 0x80},
{0xCD, 0x4B, 0xB8, 0x73, 0x3E, 0x9E, 0x61, 0x84},
{0x01, 0x7B, 0x99, 0x42, 0x38, 0x17, 0xE8, 0x95},
{0x05, 0x67, 0xFF, 0x4C, 0x19, 0x74, 0x88, 0xED},
{0x19, 0x03, 0xFD, 0x80, 0x7E, 0x44, 0xAA, 0xA3},
{0x7D, 0x0F, 0xF1, 0x84, 0x78, 0x56, 0x53, 0x32},
{0x71, 0x4D, 0xB5, 0x98, 0x5A, 0xB0, 0xA0, 0xFB},
{0x35, 0x83, 0x8A, 0xFB, 0xC4, 0x71, 0x23, 0xEA},
{0x09, 0x90, 0xB4, 0xE9, 0xD8, 0x38, 0xB1, 0x92},
{0x2D, 0xD0, 0x86, 0x90, 0x3C, 0x1C, 0x76, 0xDD},
{0xE1, 0x10, 0xA2, 0xD2, 0x2E, 0x8D, 0x4E, 0x53},
{0x65, 0x54, 0x2A, 0x1D, 0xEA, 0xC1, 0x88, 0xA0},
{0xF9, 0xA5, 0xD3, 0x91, 0x92, 0xC9, 0xAB, 0x22},
{0xDD, 0x3D, 0x22, 0xD9, 0xDC, 0xEF, 0x5A, 0xAD},
{0x51, 0x35, 0xAB, 0x3D, 0x50, 0xAF, 0xC6, 0x62},
{0x95, 0x0A, 0x58, 0x34, 0x91, 0x6C, 0xE1, 0xED},
{0xE9, 0x34, 0xB8, 0x05, 0xD6, 0x1E, 0x67, 0xA5},
{0x8D, 0x08, 0x99, 0x1C, 0x2E, 0x9A, 0x03, 0x3B},
{0xC1, 0x2A, 0xFD, 0x8E, 0xE6, 0x02, 0x12, 0x27},
};

string userinput;
int numeric_userinput;
string filename;
string filename_decryptedfile = filename + "_decrypted";
streampos size;
bool scenefileselected(0);

uint32_t address;
uint32_t addressvalue;


///"General Settings" variables and strings
bool tempsave; //0="Not Available", 1="Available". Restore most recent tempsave by setting to 1.
bool secret_reset; //0=On, 1=Off. Secret option that sets General Settings and Controller Settings back to default when the memory card save loads in-game.
bool vibration; //0=On, 1=Off.
int8_t sight_speed; //Slowest=192, Fastest=64. 50% speed = 128.
int8_t camera_speed; //Slowest=192, Fastest=64. 50% speed = 128.
int8_t mouse_speed; //Slowest = 240. Fastest = 32. 50% speed = 136.
bool camera_vertical; //0=Inverted, 1=Normal.
bool camera_horizontal; //0=Normal, 1=Inverted.
bool sight_vertical; //0=Inverted, 1=Normal.
bool sight_horizontal; //0=Normal, 1=Inverted.
bool sound_output; //0=Dolby Pro Logic II, 1=Monaural.
bool subtitles; //0=On, 1=Off
bool speakers_name; //0=On, 1=Off
bool keyboard_usage; //0=Chat Only, 1=General Controls. *May only be selected naturally in the Japanese original.
uint16_t gamma_adjustment; //20=Darkest, 180=Brightest.
uint16_t sight_support; //0=Manual, 1=Semi-Automatic, 2=Automatic. *This option does not exist in the Japanese original.
int8_t screenpos_x; //*Functionality limited to the PAL version. Signed so to allow negative values. What are the natural value ranges? -32 to 32?
int8_t screenpos_y; //*Functionality limited to the PAL version. Signed so to allow negative values. What are the natural value ranges? -32 to 32?

string tempsave_setting;
string secret_reset_setting;
string vibration_setting;
string camera_vertical_setting;
string camera_horizontal_setting;
string sight_vertical_setting;
string sight_horizontal_setting;
string sound_output_setting;
string subtitles_setting;
string speakers_name_setting;
string keyboard_usage_setting;
string sight_support_setting;


///Prototyping smaller functions.
uint32_t LittleEndianToBigEndian (char Byte1, char Byte2, char Byte3, char Byte4);
uint32_t LittleEndianToBigEndian_unsignedchar (uint8_t Byte1, uint8_t Byte2, uint8_t Byte3, uint8_t Byte4);


void deletedecodedfiles()
{
    remove("BISLPM-66271000_decrypted");
    remove("BISLPM-66271001_decrypted");
    remove("BASLUS-21419000_decrypted");
    remove("BASLUS-21419001_decrypted");
    remove("BESLES-54185000_decrypted");
    remove("BESLES-54185001_decrypted");
    remove("BISLPM-66629000_decrypted");
    remove("BISLPM-66629001_decrypted");
    remove("cfg000_decrypted");
    remove("scene000_decrypted");
    remove("scene001_decrypted");
    remove("scene002_decrypted");
    remove("scene003_decrypted");
    remove("scene004_decrypted");
    remove("scene005_decrypted");
    remove("scene006_decrypted");
    remove("scene007_decrypted");
    remove("scene008_decrypted");
    remove("scene009_decrypted");
    remove("scene010_decrypted");
    remove("scr000_decrypted");
}


void opendecodeandcopyfile(string filename) ///For non-scene0XX files
{

///OPENING ENCODED FILE AND COPYING CONTENTS INTO EncodeToDecodeMemBlock
    char *EncodeToDecodeMemBlock;
    ifstream ReadingEncodedFile;
    ReadingEncodedFile.open (filename, ios::in | ios::binary | ios::ate);
    size = ReadingEncodedFile.tellg();
    ReadingEncodedFile.seekg(0, ios::beg);
    EncodeToDecodeMemBlock = new char [size];
    ReadingEncodedFile.read (EncodeToDecodeMemBlock, size);
    ReadingEncodedFile.close();
///CONTENTS ARE NOW COPIED ONTO EncodeToDecodeMemBlock

///DECODING THE ENCODED INFORMATION NOW IN EncodeToDecodeMemBlock
    uint32_t ByteCounter(0), BlockCounter(0), KeyBlockCtr(0);
    uint32_t Gear1(0), Gear2(0);
    uint32_t TBlockA(0), TBlockB(0), KBlockA(0), KBlockB(0);
    bool CarryFlag1(0), CarryFlag2(0);
    uint8_t IntermediateCarrier(0);
    uint8_t OldMemblockValue(0);

    union u {
    uint64_t Cog64B;
    uint32_t Cog32BArray[2];
    }o;

///OUTERMOST LOOP OF THE DECODER
    for ( ; ByteCounter < size; BlockCounter++, KeyBlockCtr++)
        {
            if(KeyBlockCtr > 31)
                KeyBlockCtr = 0;

        o.Cog64B = (uint64_t)ByteCounter << 0x14;
        Gear1 = o.Cog32BArray[0] | (ByteCounter << 0x0A) | ByteCounter; ///Will this work badly when Gear1 becomes higher than 7FFFFFFF?
            if (Gear1 > ~0xA1652347)
                CarryFlag1 = 1;
            else
                CarryFlag1 = 0;
        Gear1 = Gear1 + 0xA1652347;
        Gear2 = (BlockCounter*2 | o.Cog32BArray[1]) + CarryFlag1;

///THE INNER LOOP OF THE DECODER
                for(int iterate(0), BlockwiseByteCounter(0); BlockwiseByteCounter < 8; )
                {
                    if(iterate==0 && BlockwiseByteCounter==0)
                        {
                        OldMemblockValue = EncodeToDecodeMemBlock[ByteCounter];
                        EncodeToDecodeMemBlock[ByteCounter] = 0x45 ^ BlockCounter ^ EncodeToDecodeMemBlock[ByteCounter];
                        iterate++;
                        }
                    else if(iterate==0 && BlockwiseByteCounter < 8)
                        {
                        IntermediateCarrier = EncodeToDecodeMemBlock[ByteCounter] ^ OldMemblockValue;
                        OldMemblockValue = EncodeToDecodeMemBlock[ByteCounter];
                        EncodeToDecodeMemBlock[ByteCounter] = IntermediateCarrier;
                        iterate++;
                        }
                    else if(iterate < 9 && BlockwiseByteCounter < 8)
                        {EncodeToDecodeMemBlock[ByteCounter] = 0x78 + EncodeToDecodeMemBlock[ByteCounter] - KeyBlocksArray[KeyBlockCtr][iterate-1];
                        iterate++;}
                    else if(iterate==9)
                        {iterate = 0;
                        ByteCounter++;
                        BlockwiseByteCounter++;}
                }
///EXITING THE INNER LOOP OF THE DECOER


///RESUMING THE OUTER LOOP
    ByteCounter -=8;

    TBlockA = LittleEndianToBigEndian (EncodeToDecodeMemBlock[ByteCounter], EncodeToDecodeMemBlock[ByteCounter+1], EncodeToDecodeMemBlock[ByteCounter+2], EncodeToDecodeMemBlock[ByteCounter+3]);
    TBlockB = LittleEndianToBigEndian (EncodeToDecodeMemBlock[ByteCounter+4], EncodeToDecodeMemBlock[ByteCounter+5], EncodeToDecodeMemBlock[ByteCounter+6], EncodeToDecodeMemBlock[ByteCounter+7]);

    KBlockA = LittleEndianToBigEndian_unsignedchar (KeyBlocksArray[KeyBlockCtr][0], KeyBlocksArray[KeyBlockCtr][1], KeyBlocksArray[KeyBlockCtr][2], KeyBlocksArray[KeyBlockCtr][3]);
    KBlockB = LittleEndianToBigEndian_unsignedchar (KeyBlocksArray[KeyBlockCtr][4], KeyBlocksArray[KeyBlockCtr][5], KeyBlocksArray[KeyBlockCtr][6], KeyBlocksArray[KeyBlockCtr][7]);

    if (TBlockA < KBlockA)
        CarryFlag2 = 1;
    else
        CarryFlag2 = 0;
    TBlockA = TBlockA - KBlockA;
    TBlockB = TBlockB - KBlockB - CarryFlag2;

    TBlockA = TBlockA ^ Gear1;
    TBlockB = TBlockB ^ Gear2;

    TBlockA = KBlockA ^ TBlockA;
    TBlockB = KBlockB ^ TBlockB;

    EncodeToDecodeMemBlock[ByteCounter] =   (TBlockB & 0x000000FF);
    EncodeToDecodeMemBlock[ByteCounter+1] = (TBlockB & 0x0000FF00) >> 8;
    EncodeToDecodeMemBlock[ByteCounter+2] = (TBlockB & 0x00FF0000) >> 16;
    EncodeToDecodeMemBlock[ByteCounter+3] = (TBlockB & 0xFF000000) >> 24;
    EncodeToDecodeMemBlock[ByteCounter+4] = (TBlockA & 0x000000FF);
    EncodeToDecodeMemBlock[ByteCounter+5] = (TBlockA & 0x0000FF00) >> 8;
    EncodeToDecodeMemBlock[ByteCounter+6] = (TBlockA & 0x00FF0000) >> 16;
    EncodeToDecodeMemBlock[ByteCounter+7] = (TBlockA & 0xFF000000) >> 24;

    ByteCounter +=8;
    }
///EXITING THE OUTER LOOP. FILE HAS NOW BEEN FULLY DECODED.

///CREATING A FILE COPY OF THE DECODED INFORMATION FOR USE IN OTHER FUNCTIONS
    filename_decryptedfile = filename + "_decrypted";
    ofstream CopyingDecodedFile;
    CopyingDecodedFile.open (filename_decryptedfile, ios::out | ios::trunc | ios::binary);
    CopyingDecodedFile.write (EncodeToDecodeMemBlock, size);
    CopyingDecodedFile.close();
    delete[] EncodeToDecodeMemBlock;
}


void changeaddressvalue(int address, int newvalue) ///For encoding changes into non-scene0XX files
{
///OPENING THE DECODED FILE AND COPYING IT INTO MEMBLOCK
    char *DecodeToEncodeMemBlock;
    ifstream ReadingDecodedFile;
    ReadingDecodedFile.open (filename_decryptedfile, ios::in | ios::binary | ios::ate);
    size = ReadingDecodedFile.tellg();
    ReadingDecodedFile.seekg(0, ios::beg);
    DecodeToEncodeMemBlock = new char [size];
    ReadingDecodedFile.read (DecodeToEncodeMemBlock, size);
    ReadingDecodedFile.close();
///THE DECODED DATA IS NOW IN DecodeToEncodeMemBlock
///CHANGING THE VALUE OF AN ADDRESS
    DecodeToEncodeMemBlock[address] = newvalue;
    ///VALUE CHANGED

///CHECKSUM CALCULATOR
    uint32_t Checksum(0), MemBlockConverter(0);

    for (int iterate(0); iterate < (size-8); iterate+=4)
    {
        MemBlockConverter = DecodeToEncodeMemBlock[iterate];
        if(MemBlockConverter > 0x7F)
            MemBlockConverter = MemBlockConverter & 0x000000FF;
        Checksum = Checksum + MemBlockConverter;
    }
///CHECKSUM CALCULATED


///INSERTING CHECKSUM INTO END PORTION OF FILE
    DecodeToEncodeMemBlock[size-4] = (Checksum & 0x000000FF);
    DecodeToEncodeMemBlock[size-3] = (Checksum & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[size-2] = (Checksum & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[size-1] = (Checksum & 0xFF000000) >> 24;
///CHECKSUM INSERTED


///ENCODE the file, now that all the changes have been made and the checksum has been updated.
    uint32_t ByteCounter(0), BlockCounter(0), KeyBlockCtr(0);
    uint32_t Gear1(0), Gear2(0);
    uint32_t TBlockA(0), TBlockB(0), KBlockA(0), KBlockB(0);
    bool CarryFlag1(0), CarryFlag2(0);
    uint8_t OldMemblockValue(0);

    union u {
    uint64_t Cog64B;
    uint32_t Cog32BArray[2];
    }o;

///OUTERMOST LOOP OF THE ENCODER
for ( ; ByteCounter < size; BlockCounter++, KeyBlockCtr++)
    {


        if(KeyBlockCtr > 31)
            KeyBlockCtr = 0;

    o.Cog64B = (uint64_t)ByteCounter << 0x14;



    Gear1 = o.Cog32BArray[0] | (ByteCounter << 0x0A) | ByteCounter; ///Will this work badly when Gear1 becomes higher than 7FFFFFFF?
        if (Gear1 > ~0xA1652347)
            CarryFlag1 = 1;
        else
            CarryFlag1 = 0;
    Gear1 = Gear1 + 0xA1652347;
    Gear2 = (BlockCounter*2 | o.Cog32BArray[1]) + CarryFlag1;

    TBlockB = LittleEndianToBigEndian (DecodeToEncodeMemBlock[ByteCounter], DecodeToEncodeMemBlock[ByteCounter+1], DecodeToEncodeMemBlock[ByteCounter+2], DecodeToEncodeMemBlock[ByteCounter+3]);
    TBlockA = LittleEndianToBigEndian (DecodeToEncodeMemBlock[ByteCounter+4], DecodeToEncodeMemBlock[ByteCounter+5], DecodeToEncodeMemBlock[ByteCounter+6], DecodeToEncodeMemBlock[ByteCounter+7]);

    KBlockA = LittleEndianToBigEndian_unsignedchar (KeyBlocksArray[KeyBlockCtr][0], KeyBlocksArray[KeyBlockCtr][1], KeyBlocksArray[KeyBlockCtr][2], KeyBlocksArray[KeyBlockCtr][3]);
    KBlockB = LittleEndianToBigEndian_unsignedchar (KeyBlocksArray[KeyBlockCtr][4], KeyBlocksArray[KeyBlockCtr][5], KeyBlocksArray[KeyBlockCtr][6], KeyBlocksArray[KeyBlockCtr][7]);

    TBlockB = KBlockB ^ TBlockB;
    TBlockA = KBlockA ^ TBlockA;

    TBlockB = TBlockB ^ Gear2;
    TBlockA = TBlockA ^ Gear1;

    if (TBlockA > ~KBlockA)  ///Reverse of TBlockA < KBlockA from the Decoder.
        CarryFlag2 = 1;
    else
        CarryFlag2 = 0;

    TBlockB = TBlockB + KBlockB + CarryFlag2;       ///Reversed from subtraction to addition.
    TBlockA = TBlockA + KBlockA;                    ///Reversed from subtraction to addition.

    DecodeToEncodeMemBlock[ByteCounter] =   (TBlockA & 0x000000FF);
    DecodeToEncodeMemBlock[ByteCounter+1] = (TBlockA & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[ByteCounter+2] = (TBlockA & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[ByteCounter+3] = (TBlockA & 0xFF000000) >> 24;
    DecodeToEncodeMemBlock[ByteCounter+4] = (TBlockB & 0x000000FF);
    DecodeToEncodeMemBlock[ByteCounter+5] = (TBlockB & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[ByteCounter+6] = (TBlockB & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[ByteCounter+7] = (TBlockB & 0xFF000000) >> 24;


///INNER LOOP OF ENCODER
            for(int iterate(8), BlockwiseByteCounter(0); BlockwiseByteCounter < 8; )
            {
                if(iterate != 0 && BlockwiseByteCounter < 8)
                    {DecodeToEncodeMemBlock[ByteCounter] = DecodeToEncodeMemBlock[ByteCounter] + KeyBlocksArray[KeyBlockCtr][iterate-1] - 0x78;
                    iterate--;}
                else if(iterate == 0 && BlockwiseByteCounter==0)
                    {
                    DecodeToEncodeMemBlock[ByteCounter] = 0x45 ^ BlockCounter ^ DecodeToEncodeMemBlock[ByteCounter];
                    OldMemblockValue = DecodeToEncodeMemBlock[ByteCounter];
                    BlockwiseByteCounter++;
                    ByteCounter++;
                    iterate = 8;
                    }
                else if(iterate == 0 && BlockwiseByteCounter < 8)
                    {
                    DecodeToEncodeMemBlock[ByteCounter] = DecodeToEncodeMemBlock[ByteCounter] ^ OldMemblockValue;
                    OldMemblockValue = DecodeToEncodeMemBlock[ByteCounter];
                    iterate = 8;
                    BlockwiseByteCounter++;
                    ByteCounter++;
                    }

            }
///EXITING INNER LOOP OF ENCODER
    }
///EXITING OUTER LOOP
///ENCODING FINISHED

///ENTERING RE-ENCODED DATA INTO THE FILE
    ofstream SavingEncodedFile;
    SavingEncodedFile.open (filename, ios::out | ios::trunc | ios::binary);
    SavingEncodedFile.write (DecodeToEncodeMemBlock, size);
    SavingEncodedFile.close();
    delete[] DecodeToEncodeMemBlock;
///FINISHED ENTERING RE-ENCODED DATA INTO THE FILE

}


void opendecodeandcopyscenefile(string filename) ///For scene0XX files
{

///OPENING ENCODED FILE AND COPYING CONTENTS INTO EncodeToDecodeMemBlock
    char *EncodeToDecodeMemBlock;

    ifstream ReadingEncodedFile;
    ReadingEncodedFile.open (filename, ios::in | ios::binary | ios::ate);
    size = ReadingEncodedFile.tellg();
    ReadingEncodedFile.seekg(0, ios::beg);
    EncodeToDecodeMemBlock = new char [size];
    ReadingEncodedFile.read (EncodeToDecodeMemBlock, size);
    ReadingEncodedFile.close();
///CONTENTS ARE NOW COPIED ONTO EncodeToDecodeMemBlock

///DECODING THE ENCODED INFORMATION NOW IN EncodeToDecodeMemBlock
    uint32_t ByteCounter(0), BlockCounter(0), KeyBlockCtr(0);
    uint32_t Gear1(0), Gear2(0);
    uint32_t TBlockA(0), TBlockB(0), KBlockA(0), KBlockB(0);
    bool CarryFlag1(0), CarryFlag2(0);
    uint8_t IntermediateCarrier(0);
    uint8_t OldMemblockValue(0);

    union u {
    uint64_t Cog64B;
    uint32_t Cog32BArray[2];
    }o;

    uint32_t RegionByteCounter(0);


///OUTERMOST LOOP OF THE DECODER

for ( ; ByteCounter < size; BlockCounter++, KeyBlockCtr++)
    {

    if(RegionByteCounter == 0x948)
        {RegionByteCounter = 0;
        BlockCounter = 0;
        KeyBlockCtr = 0;}

        if(KeyBlockCtr > 31)
            KeyBlockCtr = 0;

    o.Cog64B = (uint64_t)RegionByteCounter << 0x14;

    Gear1 = o.Cog32BArray[0] | (RegionByteCounter << 0x0A) | RegionByteCounter; ///Will this work badly when Gear1 becomes higher than 7FFFFFFF?

        if (Gear1 > ~0xA1652347)
            CarryFlag1 = 1;
        else
            CarryFlag1 = 0;
    Gear1 = Gear1 + 0xA1652347;
    Gear2 = (BlockCounter*2 | o.Cog32BArray[1]) + CarryFlag1;


///THE INNER LOOP OF THE DECODER
                for(int iterate(0), BlockwiseByteCounter(0); BlockwiseByteCounter < 8; )
                {
                    if(iterate==0 && BlockwiseByteCounter==0)
                        {
                        OldMemblockValue = EncodeToDecodeMemBlock[ByteCounter];
                        EncodeToDecodeMemBlock[ByteCounter] = 0x45 ^ BlockCounter ^ EncodeToDecodeMemBlock[ByteCounter];
                        iterate++;
                        }
                    else if(iterate==0 && BlockwiseByteCounter < 8)
                        {
                        IntermediateCarrier = EncodeToDecodeMemBlock[ByteCounter] ^ OldMemblockValue;
                        OldMemblockValue = EncodeToDecodeMemBlock[ByteCounter];
                        EncodeToDecodeMemBlock[ByteCounter] = IntermediateCarrier;
                        iterate++;
                        }
                    else if(iterate < 9 && BlockwiseByteCounter < 8)
                        {EncodeToDecodeMemBlock[ByteCounter] = 0x78 + EncodeToDecodeMemBlock[ByteCounter] - KeyBlocksArray[KeyBlockCtr][iterate-1];
                        iterate++;}
                    else if(iterate==9)
                        {iterate = 0;
                        ByteCounter++;
                        BlockwiseByteCounter++;}
                }
///EXITING THE INNER LOOP OF THE DECOER


///RESUMING THE OUTER LOOP
    ByteCounter -=8;

    TBlockA = LittleEndianToBigEndian (EncodeToDecodeMemBlock[ByteCounter], EncodeToDecodeMemBlock[ByteCounter+1], EncodeToDecodeMemBlock[ByteCounter+2], EncodeToDecodeMemBlock[ByteCounter+3]);
    TBlockB = LittleEndianToBigEndian (EncodeToDecodeMemBlock[ByteCounter+4], EncodeToDecodeMemBlock[ByteCounter+5], EncodeToDecodeMemBlock[ByteCounter+6], EncodeToDecodeMemBlock[ByteCounter+7]);

    KBlockA = LittleEndianToBigEndian_unsignedchar (KeyBlocksArray[KeyBlockCtr][0], KeyBlocksArray[KeyBlockCtr][1], KeyBlocksArray[KeyBlockCtr][2], KeyBlocksArray[KeyBlockCtr][3]);
    KBlockB = LittleEndianToBigEndian_unsignedchar (KeyBlocksArray[KeyBlockCtr][4], KeyBlocksArray[KeyBlockCtr][5], KeyBlocksArray[KeyBlockCtr][6], KeyBlocksArray[KeyBlockCtr][7]);

    if (TBlockA < KBlockA)
        CarryFlag2 = 1;
    else
        CarryFlag2 = 0;
    TBlockA = TBlockA - KBlockA;
    TBlockB = TBlockB - KBlockB - CarryFlag2;

    TBlockA = TBlockA ^ Gear1;
    TBlockB = TBlockB ^ Gear2;

    TBlockA = KBlockA ^ TBlockA;
    TBlockB = KBlockB ^ TBlockB;

    EncodeToDecodeMemBlock[ByteCounter] =   (TBlockB & 0x000000FF);
    EncodeToDecodeMemBlock[ByteCounter+1] = (TBlockB & 0x0000FF00) >> 8;
    EncodeToDecodeMemBlock[ByteCounter+2] = (TBlockB & 0x00FF0000) >> 16;
    EncodeToDecodeMemBlock[ByteCounter+3] = (TBlockB & 0xFF000000) >> 24;
    EncodeToDecodeMemBlock[ByteCounter+4] = (TBlockA & 0x000000FF);
    EncodeToDecodeMemBlock[ByteCounter+5] = (TBlockA & 0x0000FF00) >> 8;
    EncodeToDecodeMemBlock[ByteCounter+6] = (TBlockA & 0x00FF0000) >> 16;
    EncodeToDecodeMemBlock[ByteCounter+7] = (TBlockA & 0xFF000000) >> 24;

    ByteCounter +=8;

    RegionByteCounter +=8;
    }
///EXITING THE OUTER LOOP. cfg000 HAS NOW BEEN FULLY DECODED.

///CREATING A FILE COPY OF THE DECODED INFORMATION FOR USE IN OTHER FUNCTIONS

    filename_decryptedfile = filename + "_decrypted";
    ofstream CopyingDecodedFile;
    CopyingDecodedFile.open (filename_decryptedfile, ios::out | ios::trunc | ios::binary);
    CopyingDecodedFile.write (EncodeToDecodeMemBlock, size);
    CopyingDecodedFile.close();
    delete[] EncodeToDecodeMemBlock;
}


void changeaddressvalueofscenefile(int address, int newvalue) ///For encoding changes into scene0XX files
{

///USER INPUT: EDIT VALUE OF PARTICULAR ADDRESSES
///ENCODER

///OPENING THE DECODED FILE AND COPYING IT INTO MEMBLOCK

    char *DecodeToEncodeMemBlock;
    ifstream ReadingDecodedFile;
    ReadingDecodedFile.open (filename_decryptedfile, ios::in | ios::binary | ios::ate);
    size = ReadingDecodedFile.tellg();
    ReadingDecodedFile.seekg(0, ios::beg);
    DecodeToEncodeMemBlock = new char [size];
    ReadingDecodedFile.read (DecodeToEncodeMemBlock, size);
    ReadingDecodedFile.close();
///THE DECODED DATA IS NOW IN MEMBLOCK
    DecodeToEncodeMemBlock[address] = newvalue;
    ///VALUE CHANGED

///CHECKSUM CALCULATOR
uint32_t ChecksumRegion1(0), ChecksumRegion2(0), ChecksumRegion3(0), ChecksumRegion4(0), ChecksumRegion5(0), ChecksumRegion6(0);
uint32_t MemBlockConverter(0);


///6 REGIONS OF THE FILE MEANS 6 CHECKSUMS


///CHECKSUM REGION1
    for (int iterate(0); iterate < (0x948-0x8); iterate+=4)
    {
        MemBlockConverter = DecodeToEncodeMemBlock[iterate];
        if(MemBlockConverter > 0x7F)
            MemBlockConverter = MemBlockConverter & 0x000000FF;
        ChecksumRegion1 = ChecksumRegion1 + MemBlockConverter;
    }

///CHECKSUM REGION2
    for (int iterate(0x948); iterate < (0x1290-0x8); iterate+=4)
    {
        MemBlockConverter = DecodeToEncodeMemBlock[iterate];
        if(MemBlockConverter > 0x7F)
            MemBlockConverter = MemBlockConverter & 0x000000FF;
        ChecksumRegion2 = ChecksumRegion2 + MemBlockConverter;
    }

///CHECKSUM REGION3
    for (int iterate(0x1290); iterate < (0x1BD8-0x8); iterate+=4)
    {
        MemBlockConverter = DecodeToEncodeMemBlock[iterate];
        if(MemBlockConverter > 0x7F)
            MemBlockConverter = MemBlockConverter & 0x000000FF;
        ChecksumRegion3 = ChecksumRegion3 + MemBlockConverter;
    }

///CHECKSUM REGION4
    for (int iterate(0x1BD8); iterate < (0x2520-0x8); iterate+=4)
    {
        MemBlockConverter = DecodeToEncodeMemBlock[iterate];
        if(MemBlockConverter > 0x7F)
            MemBlockConverter = MemBlockConverter & 0x000000FF;
        ChecksumRegion4 = ChecksumRegion4 + MemBlockConverter;
    }

///CHECKSUM REGION5
    for (int iterate(0x2520); iterate < (0x2E68-0x8); iterate+=4)
    {
        MemBlockConverter = DecodeToEncodeMemBlock[iterate];
        if(MemBlockConverter > 0x7F)
            MemBlockConverter = MemBlockConverter & 0x000000FF;
        ChecksumRegion5 = ChecksumRegion5 + MemBlockConverter;
    }

///CHECKSUM REGION6
    for (int iterate(0x2E68); iterate < (0x37B0-0x8); iterate+=4)
    {
        MemBlockConverter = DecodeToEncodeMemBlock[iterate];
        if(MemBlockConverter > 0x7F)
            MemBlockConverter = MemBlockConverter & 0x000000FF;
        ChecksumRegion6 = ChecksumRegion6 + MemBlockConverter;
    }
///CHECKSUMS CALCULATED


///CheckSumRegion1 into Region1
    DecodeToEncodeMemBlock[0x948-4] = (ChecksumRegion1 & 0x000000FF);
    DecodeToEncodeMemBlock[0x948-3] = (ChecksumRegion1 & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[0x948-2] = (ChecksumRegion1 & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[0x948-1] = (ChecksumRegion1 & 0xFF000000) >> 24;
///CHECKSUM INSERTED


///CheckSumRegion2 into Region2
    DecodeToEncodeMemBlock[0x1290-4] = (ChecksumRegion2 & 0x000000FF);
    DecodeToEncodeMemBlock[0x1290-3] = (ChecksumRegion2 & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[0x1290-2] = (ChecksumRegion2 & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[0x1290-1] = (ChecksumRegion2 & 0xFF000000) >> 24;
///CHECKSUM INSERTED


///CheckSumRegion3 into Region3
    DecodeToEncodeMemBlock[0x1BD8-4] = (ChecksumRegion3 & 0x000000FF);
    DecodeToEncodeMemBlock[0x1BD8-3] = (ChecksumRegion3 & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[0x1BD8-2] = (ChecksumRegion3 & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[0x1BD8-1] = (ChecksumRegion3 & 0xFF000000) >> 24;
///CHECKSUM INSERTED


///CheckSumRegion4 into Region4
    DecodeToEncodeMemBlock[0x2520-4] = (ChecksumRegion4 & 0x000000FF);
    DecodeToEncodeMemBlock[0x2520-3] = (ChecksumRegion4 & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[0x2520-2] = (ChecksumRegion4 & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[0x2520-1] = (ChecksumRegion4 & 0xFF000000) >> 24;
///CHECKSUM INSERTED


///CheckSumRegion5 into Region5
    DecodeToEncodeMemBlock[0x2E68-4] = (ChecksumRegion5 & 0x000000FF);
    DecodeToEncodeMemBlock[0x2E68-3] = (ChecksumRegion5 & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[0x2E68-2] = (ChecksumRegion5 & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[0x2E68-1] = (ChecksumRegion5 & 0xFF000000) >> 24;
///CHECKSUM INSERTED


///CheckSumRegion6 into Region6
    DecodeToEncodeMemBlock[0x37B0-4] = (ChecksumRegion6 & 0x000000FF);
    DecodeToEncodeMemBlock[0x37B0-3] = (ChecksumRegion6 & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[0x37B0-2] = (ChecksumRegion6 & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[0x37B0-1] = (ChecksumRegion6 & 0xFF000000) >> 24;
///CHECKSUM INSERTED


///ENCODE the file, now that all the changes have been made and the checksum has been updated.
    uint32_t ByteCounter(0), BlockCounter(0), KeyBlockCtr(0);
    uint32_t Gear1(0), Gear2(0);
    uint32_t TBlockA(0), TBlockB(0), KBlockA(0), KBlockB(0);
    bool CarryFlag1(0), CarryFlag2(0);
    uint8_t OldMemblockValue(0);

    uint32_t RegionByteCounter(0);


    union u {
    uint64_t Cog64B;
    uint32_t Cog32BArray[2];
    }o;
///OUTERMOST LOOP OF THE ENCODER

for ( ; ByteCounter < size; BlockCounter++, KeyBlockCtr++)
    {

    if(RegionByteCounter == 0x948)
        {RegionByteCounter = 0;
        BlockCounter = 0;
        KeyBlockCtr = 0;}

        if(KeyBlockCtr > 31)
            KeyBlockCtr = 0;

    o.Cog64B = (uint64_t)RegionByteCounter << 0x14;


    Gear1 = o.Cog32BArray[0] | (RegionByteCounter << 0x0A) | RegionByteCounter; ///Will this work badly when Gear1 becomes higher than 7FFFFFFF?
        if (Gear1 > ~0xA1652347)
            CarryFlag1 = 1;
        else
            CarryFlag1 = 0;
    Gear1 = Gear1 + 0xA1652347;
    Gear2 = (BlockCounter*2 | o.Cog32BArray[1]) + CarryFlag1;

    TBlockB = LittleEndianToBigEndian (DecodeToEncodeMemBlock[ByteCounter], DecodeToEncodeMemBlock[ByteCounter+1], DecodeToEncodeMemBlock[ByteCounter+2], DecodeToEncodeMemBlock[ByteCounter+3]);
    TBlockA = LittleEndianToBigEndian (DecodeToEncodeMemBlock[ByteCounter+4], DecodeToEncodeMemBlock[ByteCounter+5], DecodeToEncodeMemBlock[ByteCounter+6], DecodeToEncodeMemBlock[ByteCounter+7]);

    KBlockA = LittleEndianToBigEndian_unsignedchar (KeyBlocksArray[KeyBlockCtr][0], KeyBlocksArray[KeyBlockCtr][1], KeyBlocksArray[KeyBlockCtr][2], KeyBlocksArray[KeyBlockCtr][3]);
    KBlockB = LittleEndianToBigEndian_unsignedchar (KeyBlocksArray[KeyBlockCtr][4], KeyBlocksArray[KeyBlockCtr][5], KeyBlocksArray[KeyBlockCtr][6], KeyBlocksArray[KeyBlockCtr][7]);

    TBlockB = KBlockB ^ TBlockB;
    TBlockA = KBlockA ^ TBlockA;

    TBlockB = TBlockB ^ Gear2;
    TBlockA = TBlockA ^ Gear1;

    if (TBlockA > ~KBlockA)  ///Reverse of TBlockA < KBlockA from the Decoder.
        CarryFlag2 = 1;
    else
        CarryFlag2 = 0;

    TBlockB = TBlockB + KBlockB + CarryFlag2;       ///Reversed from subtraction to addition.
    TBlockA = TBlockA + KBlockA;                    ///Reversed from subtraction to addition.

    DecodeToEncodeMemBlock[ByteCounter] =   (TBlockA & 0x000000FF);
    DecodeToEncodeMemBlock[ByteCounter+1] = (TBlockA & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[ByteCounter+2] = (TBlockA & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[ByteCounter+3] = (TBlockA & 0xFF000000) >> 24;
    DecodeToEncodeMemBlock[ByteCounter+4] = (TBlockB & 0x000000FF);
    DecodeToEncodeMemBlock[ByteCounter+5] = (TBlockB & 0x0000FF00) >> 8;
    DecodeToEncodeMemBlock[ByteCounter+6] = (TBlockB & 0x00FF0000) >> 16;
    DecodeToEncodeMemBlock[ByteCounter+7] = (TBlockB & 0xFF000000) >> 24;


///INNER LOOP OF ENCODER
    for(int iterate(8), BlockwiseByteCounter(0); BlockwiseByteCounter < 8; )
    {
         if(iterate != 0 && BlockwiseByteCounter < 8)
            {DecodeToEncodeMemBlock[ByteCounter] = DecodeToEncodeMemBlock[ByteCounter] + KeyBlocksArray[KeyBlockCtr][iterate-1] - 0x78;
            iterate--;}
         else if(iterate == 0 && BlockwiseByteCounter==0)
            {
            DecodeToEncodeMemBlock[ByteCounter] = 0x45 ^ BlockCounter ^ DecodeToEncodeMemBlock[ByteCounter];
            OldMemblockValue = DecodeToEncodeMemBlock[ByteCounter];
            BlockwiseByteCounter++;
            ByteCounter++;
            iterate = 8;
            }
        else if(iterate == 0 && BlockwiseByteCounter < 8)
            {
            DecodeToEncodeMemBlock[ByteCounter] = DecodeToEncodeMemBlock[ByteCounter] ^ OldMemblockValue;
            OldMemblockValue = DecodeToEncodeMemBlock[ByteCounter];
            iterate = 8;
            BlockwiseByteCounter++;
            ByteCounter++;
            }

    }

    RegionByteCounter +=8;

///EXITING INNER LOOP OF ENCODER

    }
///EXITING OUTER LOOP
///ENCODING FINISHED

///ENTERING RE-ENCODED DATA INTO THE FILE
    ofstream SavingEncodedFile;
    SavingEncodedFile.open (filename, ios::out | ios::trunc | ios::binary);
    SavingEncodedFile.write (DecodeToEncodeMemBlock, size);
    SavingEncodedFile.close();
    delete[] DecodeToEncodeMemBlock;
///FINISHED ENTERING RE-ENCODED DATA INTO THE FILE
}


int checkfileaddressvalue(int address)
{
///OPENING DECODED FILE AND COPYING CONTENTS INTO DecodedMemBlock
    char *DecodedMemBlock;
    ifstream ReadingDecodedFile;
    ReadingDecodedFile.open (filename_decryptedfile, ios::in | ios::binary | ios::ate);
    size = ReadingDecodedFile.tellg();
    ReadingDecodedFile.seekg(0, ios::beg);
    DecodedMemBlock = new char [size];
    ReadingDecodedFile.read (DecodedMemBlock, size);
    ReadingDecodedFile.close();
///CONTENTS ARE NOW COPIED ONTO DecodedMemBlock
    addressvalue = DecodedMemBlock[address] & 0xFF;
    delete[] DecodedMemBlock;
    return addressvalue;
}




int main () ///MAIN IS FOR THE USER SELECTION TREES AND USER INPUTS
{
    cout << "\nWELCOME TO McDIRGE: VERSION 0.3, COMMAND LINE EDITION" << endl;
    cout << "Save editor for Dirge of Cerberus: Final Fantasy VII.\n" << endl;

    cout << "Now user friendly for editing the Configuration file (cfg000)." << endl;
    cout << "Advanced mode allows you to edit any byte in a greater selection of files." << endl;
    cout << "Consult the ReadMe for further instructions.\n" << endl;

    topmenu:

    cout << "\nType and enter the symbol(s) shown within arrow brackets to make a selection.\n" << endl;

    cout << "<cfg>\tConfiguration file (cfg000)\n<adv>\tAdvanced Mode\n<exit>\tExit program" << endl;

    cin >> userinput;

        if(userinput == "cfg" || userinput == "<cfg>" || userinput == "cfg>" || userinput == "<cfg")
            {
            filename = "cfg000";
            ///FUNCTION THAT OPENS cfg000, DECRYPTS, AND CREATES DECRYPTED FILE COPY
            opendecodeandcopyfile(filename);
            Tree_cfg:

            cout << "\n'Configuration file (cfg000)'\n" << endl;
            cout << "\t<gen>\tGeneral Settings\n\t<ctr>\tController Setup **STILL UNDER CONSTRUCTION**\n\t<top>\tGo back to top menu\n\t<exit>\tExit program" << endl;
            cin >> userinput;

            if(userinput == "gen" || userinput == "<gen>" || userinput == "gen>" || userinput == "<gen")
                {
                cout << "\nSelected 'General Settings'." << endl;
                cout << "In-game these constitute 'Control Settings'(Page 1) & 'Other Settings'(Page 5)." << endl;
                Tree_gen:
                cout << "\nType and enter the symbol(s) shown within arrow brackets to change a setting.\n" << endl;

                ///FUNCTIONS TO CHECK RELEVANT VALUES IN DECRYPTED FILE COPY
                tempsave            = checkfileaddressvalue(0x4);
                secret_reset        = checkfileaddressvalue(0x40);
                vibration           = checkfileaddressvalue(0x41);
                sight_speed         = checkfileaddressvalue(0x43);
                camera_speed        = checkfileaddressvalue(0x44);
                mouse_speed         = checkfileaddressvalue(0x46);
                camera_vertical     = checkfileaddressvalue(0x48);
                camera_horizontal   = checkfileaddressvalue(0x49);
                sight_vertical      = checkfileaddressvalue(0x4A);
                sight_horizontal    = checkfileaddressvalue(0x4B);
                sound_output        = checkfileaddressvalue(0x4C);
                subtitles           = checkfileaddressvalue(0x4D);
                speakers_name       = checkfileaddressvalue(0x4E);
                keyboard_usage      = checkfileaddressvalue(0x50);
                gamma_adjustment    = checkfileaddressvalue(0x5A);
                sight_support       = checkfileaddressvalue(0x5E);
                screenpos_x         = checkfileaddressvalue(0x5F);
                screenpos_y         = checkfileaddressvalue(0x60);

                ///
                    {
                    if (tempsave == 0)
                        tempsave_setting = "Not Available";
                    else if (tempsave == 1)
                        tempsave_setting = "Available";
                    else
                        tempsave_setting = "INVALID";

                    if (secret_reset == 0)
                        secret_reset_setting = "On";
                    else if (secret_reset == 1)
                        secret_reset_setting = "Off";
                    else
                        secret_reset_setting = "INVALID";

                    if (vibration == 0)
                        vibration_setting = "On";
                    else if (vibration == 1)
                        vibration_setting = "Off";
                    else
                        vibration_setting = "INVALID";

                    if (camera_vertical == 0)
                        camera_vertical_setting = "Inverted";
                    else if (camera_vertical == 1)
                        camera_vertical_setting = "Normal";
                    else
                        camera_vertical_setting = "INVALID";

                    if (camera_horizontal == 0)
                        camera_horizontal_setting = "Normal";
                    else if (camera_horizontal == 1)
                        camera_horizontal_setting = "Inverted";
                    else
                        camera_horizontal_setting = "INVALID";

                    if (sight_vertical == 0)
                        sight_vertical_setting = "Inverted";
                    else if (sight_vertical == 1)
                        sight_vertical_setting = "Normal";
                    else
                        sight_vertical_setting = "INVALID";

                    if (sight_horizontal == 0)
                        sight_horizontal_setting = "Normal";
                    else if (sight_horizontal == 1)
                        sight_horizontal_setting = "Inverted";
                    else
                        sight_horizontal_setting = "INVALID";

                    if (sound_output == 0)
                        sound_output_setting = "Dolby Pro Logic II";
                    else if (sound_output == 1)
                        sound_output_setting = "Monaural";
                    else
                        sound_output_setting = "INVALID";

                    if (subtitles == 0)
                        subtitles_setting = "On";
                    else if (subtitles == 1)
                        subtitles_setting = "Off";
                    else
                        subtitles_setting = "INVALID";

                    if (speakers_name == 0)
                        speakers_name_setting = "On";
                    else if (speakers_name == 1)
                        speakers_name_setting = "Off";
                    else
                        speakers_name_setting = "INVALID";

                    if (keyboard_usage == 0)
                        keyboard_usage_setting = "Chat Only";
                    else if (keyboard_usage == 1)
                        keyboard_usage_setting = "General Controls";
                    else
                        keyboard_usage_setting = "INVALID";

                    if (sight_support == 0)
                        sight_support_setting = "Manual";
                    else if (sight_support == 1)
                        sight_support_setting = "Semi-Automatic";
                    else if (sight_support == 2)
                        sight_support_setting = "Automatic";
                    else
                        sight_support_setting = "INVALID";
                    }

                ///

                cout << "\tSETTING\t\t\tCURRENT STATUS" << endl;
                cout << "<tp>\tTempsave:\t\t" << tempsave_setting << endl;
                cout << "<rs>\tReset to default:\t" << secret_reset_setting << endl;
                cout << "<vb>\tVibration:\t\t" << vibration_setting << endl;
                cout << "<ss>\tSight Speed:\t\t" << (int)sight_speed << endl;
                cout << "<cs>\tCamera Speed:\t\t" << (int)camera_speed << endl;
                cout << "<ms>\tMouse Speed:\t\t" << (int)mouse_speed << endl;
                cout << "<cv>\tCamera - Vertical:\t" << camera_vertical_setting << endl;
                cout << "<ch>\tCamera - Horizontal:\t" << camera_horizontal_setting << endl;
                cout << "<sv>\tSight - Vertical:\t" << sight_vertical_setting << endl;
                cout << "<sh>\tSight - Horizontal:\t" << sight_horizontal_setting << endl;
                cout << "<so>\tSound Output:\t\t" << sound_output_setting << endl;
                cout << "<sb>\tSubtitles:\t\t" << subtitles_setting << endl;
                cout << "<sn>\tSpeaker's Name:\t\t" << speakers_name_setting << endl;
                cout << "<ku>\tKeyboard Usage:\t\t" << keyboard_usage_setting << endl;
                cout << "<ga>\tGamma Adjustment:\t" << gamma_adjustment << endl;
                cout << "<ssu>\tSight Support:\t\t" << sight_support_setting << endl;
                cout << "<spx>\tScreen Position: X axis\t\t" << (int)screenpos_x << "\t*PAL-exclusive functionality" << endl;
                cout << "<spy>\tScreen Position: Y axis\t\t" << (int)screenpos_y << "\t*PAL-exclusive functionality\n" << endl;

                cout << "<top>\tGo back to top menu\n<exit>\tExit program\n" << endl;

                cin >> userinput;

                    if (userinput == "tp" || userinput == "tp>" || userinput == "<tp" || userinput == "<tp>")
                        {
                        cout << "\n\tTempsave is '" << tempsave_setting << "' at this point." << endl;
                        cout << "Value Guide: 0 = 'Tempsave NOT available', 1 = 'Tempsave available'" << endl;
                        cout << "The most recent Tempsave data can be restored by setting this value to 1." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        tempsave = numeric_userinput;
                        stringstream(userinput) >> numeric_userinput;
                        tempsave = numeric_userinput;
                        ///FUNCTION THAT CHANGES THE NEW VALUE INTO cfg000
                        changeaddressvalue(0x4, tempsave);
                        ///RE-DECODES cfg000 TO CONFIRM THE NEW VALUE
                        opendecodeandcopyfile(filename);
                        ///Retrieving the new value as shown in the decoded file, confirming it rather than assuming.
                        tempsave = checkfileaddressvalue(0x4);
                        if (tempsave == 0)
                            tempsave_setting = "Not Available";
                        else if
                            (tempsave == 1)
                            tempsave_setting = "Available";
                        else
                            tempsave_setting = "INVALID";
                        cout << "Tempsave is now '" << tempsave_setting << "'." << endl;
                        goto Tree_gen;
                        }

                    else if (userinput == "rs" || userinput == "rs>" || userinput == "<rs" || userinput == "<rs>")
                        {
                        cout << "\n\tReset to default is '" << secret_reset_setting << "' at this point." << endl;
                        cout << "Value Guide: 0 = 'Reset will happen', 1 = 'Reset will NOT happen'" << endl;
                        cout << "If set to 0, the game will reset the following settings when you load a save file:" << endl;
                        cout << "\t-General Settings\n\t-Controller Settings\n\t-Keyboard Settings\n\t-Mouse Settings" << endl;
                        cout << "This is a secret switch not available to the player." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        secret_reset = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x40, secret_reset);
                        opendecodeandcopyfile(filename);
                        secret_reset = checkfileaddressvalue(0x40);
                        if (secret_reset == 0)
                            secret_reset_setting = "On";
                        else if
                            (secret_reset == 1)
                            secret_reset_setting = "Off";
                        else
                            secret_reset_setting = "INVALID";
                        cout << "Reset to default is now '" << secret_reset_setting << "'." << endl;
                        goto Tree_gen;
                        }

                    else if (userinput == "vb" || userinput == "vb>" || userinput == "<vb" || userinput == "<vb>")
                       {
                        cout << "\n\tVibration is '" << vibration_setting << "' at this point." << endl;
                        cout << "Value Guide: 0 = 'On', 1 = 'Off'" << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        vibration = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x41, vibration);
                        opendecodeandcopyfile(filename);
                        vibration = checkfileaddressvalue(0x41);
                        if (vibration == 0)
                            vibration_setting = "On";
                        else if
                            (vibration == 1)
                            vibration_setting = "Off";
                        else
                            vibration_setting = "INVALID";
                        cout << "Vibration is now '" << vibration_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "ss" || userinput == "ss>" || userinput == "<ss" || userinput == "<ss>")
                       {
                        cout << "\n\tSight Speed is '" << (int)sight_speed << "' at this point." << endl;
                        cout << "Value Guide:" << endl;
                        cout << "0%\t= -64 [Slowest]\n50%\t= 0 [Default]\n100%\t= 64 [Fastest]" << endl;
                        cout << "In-game you are allowed to change this value in increments of 8." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        sight_speed = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x43, sight_speed);
                        opendecodeandcopyfile(filename);
                        sight_speed = checkfileaddressvalue(0x43);
                        cout << "Sight Speed has now been set to value '" << (int)sight_speed << "'." << endl;
                        if (sight_speed < -64 || sight_speed > 64)
                            cout << "Keep in mind this value is outside of the normal range." << endl;

                        goto Tree_gen;
                       }

                    else if (userinput == "cs" || userinput == "cs>" || userinput == "<cs" || userinput == "<cs>")
                       {
                        cout << "\n\tCamera Speed is '" << (int)camera_speed << "' at this point." << endl;
                        cout << "Value guide:" << endl;
                        cout << "0%\t= -64 [Slowest]\n50%\t= 0 [Default]\n100%\t= 64 [Fastest]" << endl;
                        cout << "In-game you are allowed to change this value in increments of 8." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        camera_speed = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x44, camera_speed);
                        opendecodeandcopyfile(filename);
                        camera_speed = checkfileaddressvalue(0x44);
                        cout << "Camera Speed has now been set to value '" << (int)camera_speed << "'." << endl;
                        if (camera_speed < -64 || camera_speed > 64)
                            cout << "Keep in mind this value is outside of the normal range." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "ms" || userinput == "ms>" || userinput == "<ms" || userinput == "<ms>")
                       {
                        cout << "\n\tMouse Speed is '" << (int)mouse_speed << "' at this point." << endl;
                        cout << "Value guide:" << endl;
                        cout << "0%\t\t= -16 [Slowest]\n25%\t\t= -4\n33.3...%\t= 0 [Default]\n50%\t\t= 8\n75%\t\t= 20\n100%\t\t= 32 [Fastest]" << endl;
                        cout << "In-game you are allowed to change this value in increments of 1." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        mouse_speed = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x46, mouse_speed);
                        opendecodeandcopyfile(filename);
                        mouse_speed = checkfileaddressvalue(0x46);
                        cout << "Mouse Speed has now been set to value '" << (int)mouse_speed << "'." << endl;
                        if (mouse_speed < -16 || mouse_speed > 32)
                            cout << "Keep in mind this value is outside of the normal range." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "cv" || userinput == "cv>" || userinput == "<cv" || userinput == "<cv>")
                       {
                        cout << "\n\tCamera - Vertical is '" << camera_vertical_setting << "' at this point." << endl;
                        cout << "Value guide: 0 = 'Inverted', 1 = 'Normal'" << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        camera_vertical = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x48, camera_vertical);
                        opendecodeandcopyfile(filename);
                        camera_vertical = checkfileaddressvalue(0x48);
                        if (camera_vertical == 0)
                            camera_vertical_setting = "Inverted";
                        else if (camera_vertical == 1)
                            camera_vertical_setting = "Normal";
                        else
                            camera_vertical_setting = "INVALID";
                        cout << "Camera - Vertical is now set to '" << camera_vertical_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "ch" || userinput == "ch>" || userinput == "<ch" || userinput == "<ch>")
                       {
                        cout << "\n\tCamera - Horizontal is '" << camera_horizontal_setting << "' at this point." << endl;
                        cout << "Value guide: 0 = 'Normal', 1 = 'Inverted'" << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        camera_horizontal = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x49, camera_horizontal);
                        opendecodeandcopyfile(filename);
                        camera_horizontal = checkfileaddressvalue(0x49);
                        if (camera_horizontal == 0)
                            camera_horizontal_setting = "Normal";
                        else if (camera_horizontal == 1)
                            camera_horizontal_setting = "Inverted";
                        else
                            camera_horizontal_setting = "INVALID";
                        cout << "Camera - Horizontal is now set to '" << camera_horizontal_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "sv" || userinput == "sv>" || userinput == "<sv" || userinput == "<sv>")
                       {
                        cout << "\n\tSight - Vertical is '" << sight_vertical_setting << "' at this point." << endl;
                        cout << "Value guide: 0 = 'Inverted', 1 = 'Normal'" << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        sight_vertical = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x4A, sight_vertical);
                        opendecodeandcopyfile(filename);
                        sight_vertical = checkfileaddressvalue(0x4A);
                        if (sight_vertical == 0)
                            sight_vertical_setting = "Inverted";
                        else if (sight_vertical == 1)
                            sight_vertical_setting = "Normal";
                        else
                            sight_vertical_setting = "INVALID";
                        cout << "Sight - Vertical is now set to '" << sight_vertical_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "sh" || userinput == "sh>" || userinput == "<sh" || userinput == "<sh>")
                       {
                        cout << "\n\tSight - Horizontal is '" << sight_horizontal_setting << "' at this point." << endl;
                        cout << "Value guide: 0 = 'Normal', 1 = 'Inverted'" << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        sight_horizontal = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x4B, sight_horizontal);
                        opendecodeandcopyfile(filename);
                        sight_horizontal = checkfileaddressvalue(0x4B);
                        if (sight_horizontal == 0)
                            sight_horizontal_setting = "Normal";
                        else if (sight_horizontal == 1)
                            sight_horizontal_setting = "Inverted";
                        else
                            sight_horizontal_setting = "INVALID";
                        cout << "Sight - Horizontal is now set to '" << sight_horizontal_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "so" || userinput == "so>" || userinput == "<so" || userinput == "<so>")
                       {
                        cout << "\n\tSound Output is '" << sound_output_setting << "' at this point." << endl;
                        cout << "Value guide: 0 = 'Dolby Pro Logic II', 1 = 'Monaural'" << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        sound_output = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x4C, sound_output);
                        opendecodeandcopyfile(filename);
                        sound_output = checkfileaddressvalue(0x4C);
                        if (sound_output == 0)
                            sound_output_setting = "Dolby Pro Logic II";
                        else if (sound_output == 1)
                            sound_output_setting = "Monaural";
                        else
                            sound_output_setting = "INVALID";
                        cout << "Sound Output is now set to '" << sound_output_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "sb" || userinput == "sb>" || userinput == "<sb" || userinput == "<sb>")
                       {
                        cout << "\n\tSubtitles are '" << subtitles_setting << "' at this point." << endl;
                        cout << "Value Guide: 0 = 'On', 1 = 'Off'" << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        subtitles = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x4D, subtitles);
                        opendecodeandcopyfile(filename);
                        subtitles = checkfileaddressvalue(0x4D);
                        if (subtitles == 0)
                            subtitles_setting = "On";
                        else if (subtitles == 1)
                            subtitles_setting = "Off";
                        else
                            subtitles_setting = "INVALID";
                        cout << "Subtitles are now set to '" << subtitles_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "sn" || userinput == "sn>" || userinput == "<sn" || userinput == "<sn>")
                       {
                        cout << "\n\tSpeaker's Name is '" << speakers_name_setting << "' at this point." << endl;
                        cout << "Value Guide: 0 = 'On', 1 = 'Off'" << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        speakers_name = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x4E, speakers_name);
                        opendecodeandcopyfile(filename);
                        speakers_name = checkfileaddressvalue(0x4E);
                        if (speakers_name == 0)
                            speakers_name_setting = "On";
                        else if (speakers_name == 1)
                            speakers_name_setting = "Off";
                        else
                            speakers_name_setting = "INVALID";
                        cout << "Speaker's Name is now set to '" << speakers_name_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "ku" || userinput == "ku>" || userinput == "<ku" || userinput == "<ku>")
                       {
                        cout << "\n\tKeyboard Usage is set to '" << keyboard_usage_setting << "' at this point." << endl;
                        cout << "Value guide: 0 = 'Chat Only', 1 = 'General Controls'" << endl;
                        cout << "The Keyboard Usage setting is functional in all versions, but only the JP original shows it in the menu." << endl;
                        cout << "If you select 'Chat Only' then you can't control Vincent with the keyboard." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        keyboard_usage = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x50, keyboard_usage);
                        opendecodeandcopyfile(filename);
                        keyboard_usage = checkfileaddressvalue(0x50);
                        if (keyboard_usage == 0)
                            keyboard_usage_setting = "Chat Only";
                        else if (keyboard_usage == 1)
                            keyboard_usage_setting = "General Controls";
                        else
                            keyboard_usage_setting = "INVALID";
                        cout << "Keyboard Usage is now set to '" << keyboard_usage_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "ga" || userinput == "ga>" || userinput == "<ga" || userinput == "<ga>")
                       {
                        cout << "\n\tGamma Adjustment is '" << gamma_adjustment << "' at this point." << endl;
                        cout << "Value guide:" << endl;
                        cout << "0%\t= 20 [Darkest]\n25%\t= 60\n50%\t= 100\n67.5%\t= 128 [Default]\n75%\t= 140\n100%\t= 180 [Brightest]" << endl;
                        cout << "In-game you are allowed to change this value in increments of 1." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        gamma_adjustment = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x5A, gamma_adjustment);
                        opendecodeandcopyfile(filename);
                        gamma_adjustment = checkfileaddressvalue(0x5A);
                        cout << "Gamma Adjustment is now set to '" << gamma_adjustment << "'." << endl;
                        if (gamma_adjustment < 20 || gamma_adjustment > 180)
                            cout << "Keep in mind this value is outside of the normal range." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "ssu" || userinput == "ssu>" || userinput == "<ssu" || userinput == "<ssu>")
                       {
                        cout << "\n\tSight Support is set to '" << sight_support_setting << "' at this point." << endl;
                        cout << "Value guide: 0 = 'Manual', 1 = 'Semi-Automatic', 2 = 'Automatic'" << endl;
                        cout << "In the original JP version this menu option does not exist. Auto-lock is added with equippable scopes." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        sight_support = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x5E, sight_support);
                        opendecodeandcopyfile(filename);
                        sight_support = checkfileaddressvalue(0x5E);
                        if (sight_support == 0)
                            sight_support_setting = "Manual";
                        else if (sight_support == 1)
                            sight_support_setting = "Semi-Automatic";
                        else if (sight_support == 2)
                            sight_support_setting = "Automatic";
                        else
                            sight_support_setting = "INVALID";
                        cout << "Sight Support is now set to '" << sight_support_setting << "'." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "spx" || userinput == "spx>" || userinput == "<spx" || userinput == "<spx>")
                       {
                        cout << "\n\tScreen Position: X axis is at value '" << (int)screenpos_x << "'." << endl;
                        cout << "Value guide:" << endl;
                        cout << "-32\t[Leftmost]\n0\t[Default]\n32\t[Rightmost]" << endl;
                        cout << "In-game you are allowed to change this value in increments of 1." << endl;
                        cout << "This setting only has functionality in the PAL (European/Australian/Misc) version of the game." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        screenpos_x = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x5F, screenpos_x);
                        opendecodeandcopyfile(filename);
                        screenpos_x = checkfileaddressvalue(0x5F);
                        cout << "Screen Position: X axis is now set to value '" << (int)screenpos_x << "'." << endl;
                        if (screenpos_x < -32 || screenpos_x > 32)
                            cout << "Keep in mind this value is outside of the normal range." << endl;
                        goto Tree_gen;
                       }

                    else if (userinput == "spy" || userinput == "spy>" || userinput == "<spy" || userinput == "<spy>")
                       {
                        cout << "\n\tScreen Position: Y axis is at value '" << (int)screenpos_y << "'." << endl;
                        cout << "Value guide:" << endl;
                        cout << "-32\t[Upmost]\n0\t[Default]\n32\t[Downmost]" << endl;
                        cout << "In-game you are allowed to change this value in increments of 1." << endl;
                        cout << "This setting only has functionality in the PAL (European/Australian/Misc) version of the game." << endl;
                        cout << " Enter a number:" << endl;
                        cin >> userinput;
                        stringstream(userinput) >> numeric_userinput;
                        screenpos_y = numeric_userinput;
                        ///FUNCTIONS THAT HELP SAVE THE CHANGE INTO THE FINAL CFG000 file!
                        changeaddressvalue(0x60, screenpos_y);
                        opendecodeandcopyfile(filename);
                        screenpos_y = checkfileaddressvalue(0x60);
                        cout << "Screen Position: Y axis is now set to value '" << (int)screenpos_y << "'." << endl;
                        if (screenpos_y < -32 || screenpos_y > 32)
                            cout << "Keep in mind this value is outside of the normal range." << endl;
                        goto Tree_gen;
                       }

                    else if(userinput == "top" || userinput == "<top>" || userinput == "top>" || userinput == "<top")
                        {
                        deletedecodedfiles();
                        goto topmenu;
                        }
                    else if (userinput == "exit" || userinput == "<exit>"|| userinput == "exit>" || userinput == "<exit")
                        {
                        deletedecodedfiles();
                        cout << "\nThank you for using McDirge. Have a good day!" << endl;
                        }
                    else
                        {
                        cout << "Invalid input. Try again." << endl;
                        goto Tree_gen;
                        }

                }


            else if(userinput == "ctr" || userinput =="<ctr>" || userinput == "ctr>" || userinput == "<ctr")
                {
                cout << "This section is still under construction. Please return to where you were." << endl;
                goto Tree_cfg;
                }

            else if(userinput == "top" || userinput == "<top>" || userinput == "top>" || userinput == "<top")
                {
                deletedecodedfiles();
                goto topmenu;
                }
            else if (userinput == "exit" || userinput == "<exit>"|| userinput == "exit>" || userinput == "<exit")
                {
                deletedecodedfiles();
                cout << "\nThank you for using McDirge. Have a good day!" << endl;
                }

            else
                {
                cout << "Invalid input. Please try again." << endl;
                goto Tree_cfg;
                }

            }


       else if(userinput == "adv" || userinput == "<adv>" || userinput == "adv>" || userinput == "<adv")
            {
            Tree_adv:
            scenefileselected = 0;
            cout << "\n'Advanced Mode'\n" << endl;

            cout << "Which file do you wish to open and edit?" << endl;
            cout << "Enter the symbols shown within arrow brackets to select the file.\n" << endl;

            cout << "\t<j00>\t\tBISLPM-66271000" << endl;
            cout << "\t<j01>\t\tBISLPM-66271001\n" << endl;

            cout << "\t<na0>\t\tBASLUS-21419000" << endl;
            cout << "\t<na1>\t\tBASLUS-21419001\n" << endl;

            cout << "\t<pal0>\t\tBESLES-54185000" << "\t(*Lower-case of L in pal0)" << endl;
            cout << "\t<pal1>\t\tBESLES-54185001" << "\t(*Lower-case of L in pal1)\n" << endl;

            cout << "\t<j10>\t\tBISLPM-66629000" << endl;
            cout << "\t<j11>\t\tBISLPM-66629001\n" << endl;

            cout << "\t<cfg>\t\tcfg000" << endl;
            cout << "\t<scn00>\t\tscene000" << endl;
            cout << "\t<scn01>\t\tscene001" << endl;
            cout << "\t<scn02>\t\tscene002" << endl;
            cout << "\t<scn03>\t\tscene003" << endl;
            cout << "\t<scn04>\t\tscene004" << endl;
            cout << "\t<scn05>\t\tscene005" << endl;
            cout << "\t<scn06>\t\tscene006" << endl;
            cout << "\t<scn07>\t\tscene007" << endl;
            cout << "\t<scn08>\t\tscene008" << endl;
            cout << "\t<scn09>\t\tscene009" << endl;
            cout << "\t<scn10>\t\tscene010" << endl;
            cout << "\t<scr>\t\tscr000\n" << endl;

            cout << "\t<top>\tGo back to top menu" << endl;
            cout << "\t<exit>\tExit program" << endl;

            cin >> userinput;

            if(userinput == "j00" || userinput == "j00>" || userinput == "<j00" || userinput == "<j00>")
                {
                filename = "BISLPM-66271000";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "j01" || userinput == "j01>" || userinput == "<j01" || userinput == "<j01>")
                {
                filename = "BISLPM-66271001";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "na0" || userinput == "na0>" || userinput == "<na0" || userinput == "<na0>")
                {
                filename = "BASLUS-21419000";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "na1" || userinput == "na1>" || userinput == "<na1" || userinput == "<na1>")
                {
                filename = "BASLUS-21419001";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "pal0" || userinput == "pal0>" || userinput == "<pal0" || userinput == "<pal0>")
                {
                filename = "BESLES-54185000";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "pal1" || userinput == "pal1>" || userinput == "<pal1" || userinput == "<pal1>")
                {
                filename = "BESLES-54185001";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "j10" || userinput == "j10>" || userinput == "<j10" || userinput == "<j10>")
                {
                filename = "BISLPM-66629000";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "j11" || userinput == "j11>" || userinput == "<j11" || userinput == "<j11>")
                {
                filename = "BISLPM-66629001";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "j11" || userinput == "j11>" || userinput == "<j11" || userinput == "<j11>")
                {
                filename = "BISLPM-66629001";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "cfg" || userinput == "cfg>" || userinput == "<cfg" || userinput == "<cfg>")
                {
                filename = "cfg000";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "scn00" || userinput == "scn00>" || userinput == "<scn00" || userinput == "<scn00>")
                {
                filename = "scene000";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn01" || userinput == "scn01>" || userinput == "<scn01" || userinput == "<scn01>")
                {
                filename = "scene001";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn02" || userinput == "scn02>" || userinput == "<scn02" || userinput == "<scn02>")
                {
                filename = "scene002";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn03" || userinput == "scn03>" || userinput == "<scn03" || userinput == "<scn03>")
                {
                filename = "scene003";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn04" || userinput == "scn04>" || userinput == "<scn04" || userinput == "<scn04>")
                {
                filename = "scene004";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn05" || userinput == "scn05>" || userinput == "<scn05" || userinput == "<scn05>")
                {
                filename = "scene005";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn06" || userinput == "scn06>" || userinput == "<scn06" || userinput == "<scn06>")
                {
                filename = "scene006";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn07" || userinput == "scn07>" || userinput == "<scn07" || userinput == "<scn07>")
                {
                filename = "scene007";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn08" || userinput == "scn08>" || userinput == "<scn08" || userinput == "<scn08>")
                {
                filename = "scene008";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn09" || userinput == "scn09>" || userinput == "<scn09" || userinput == "<scn09>")
                {
                filename = "scene009";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scn10" || userinput == "scn10>" || userinput == "<scn10" || userinput == "<scn10>")
                {
                filename = "scene010";
                scenefileselected = 1;
                opendecodeandcopyscenefile(filename);
                }
            else if(userinput == "scr" || userinput == "scr>" || userinput == "<scr" || userinput == "<scr>")
                {
                filename = "scr000";
                opendecodeandcopyfile(filename);
                }
            else if(userinput == "top" || userinput == "<top>" || userinput == "top>" || userinput == "<top")
                {
                deletedecodedfiles();
                goto topmenu;
                }
            else if (userinput == "exit" || userinput == "<exit>"|| userinput == "exit>" || userinput == "<exit")
                {
                deletedecodedfiles();
                cout << "\nThank you for using McDirge. Have a good day!" << endl;
                return 0;
                }
            else
                {
                cout << "Invalid input. Please try again." << endl;
                goto Tree_adv;
                }

            File_Selected:
                cout << std::hex << std::uppercase << "\nWhich address in " << filename << " do you wish to change the value of?" << endl;
                cout << "\tEnter a hex value in the range of 0x0 to 0x" << size - 9 << endl;
                cin >> std::hex >> address;
                cout << "What value do you want 0x" << address << " to have?" << endl;
                cout << "\tEnter a hex value in the range 0x0 to 0xFF" << endl;
                cin >> addressvalue;
                ///FUNCTION THAT EDITS THE VALUE AND SAVES IT INTO THE RE-ENCODED FILE
                if (scenefileselected == 0)
                    changeaddressvalue(address, addressvalue);
                else if (scenefileselected == 1)
                    changeaddressvalueofscenefile(address, addressvalue);
                else
                    {
                    cout << "Error. Return to file selection.\n" << endl;
                    goto Tree_adv;
                    }
                cout << "\nAddress 0x" << address << " now has the value 0x" << addressvalue << "." << endl;
                adv_crossroads:
                cout << "\t<e>\tEdit another address in " << filename << endl;
                cout << "\t<b>\tBack to file selection" << endl;
                cout << "\t<top>\tGo back to top menu\n\t<exit>\tExit program\n" << endl;

                cin >> userinput;

                if (userinput == "e" || userinput == "<e>"|| userinput == "e>" || userinput == "<e")
                    goto File_Selected;
                else if (userinput == "b" || userinput == "<b>"|| userinput == "b>" || userinput == "<b")
                    {
                    deletedecodedfiles();
                    goto Tree_adv;
                    }
                else if(userinput == "top" || userinput == "<top>" || userinput == "top>" || userinput == "<top")
                    {
                    deletedecodedfiles();
                    goto topmenu;
                    }
                else if (userinput == "exit" || userinput == "<exit>"|| userinput == "exit>" || userinput == "<exit")
                    {
                    deletedecodedfiles();
                    cout << "\nThank you for using McDirge. Have a good day!" << endl;
                    }
                else
                    {
                    cout << "Invalid input Try again." << endl;
                    goto adv_crossroads;
                    }


            }

        else if (userinput == "exit" || userinput == "<exit>"|| userinput == "exit>" || userinput == "<exit")
            {
            deletedecodedfiles();
            cout << "\nThank you for using McDirge. Have a good day!" << endl;
            }
        else
            {
            cout << "Invalid input Try again." << endl;
            goto topmenu;
            }

    return 0;
}




///FUNCTIONS FOR BYTE CONVERSION FROM 4-BYTE BLOCKS. FUNCTIONS PROTOTYPED AT THE TOP OF THE PROGRAM.
uint32_t LittleEndianToBigEndian (char Byte1, char Byte2, char Byte3, char Byte4)
{
    static uint32_t BigEndianBlock, ConvertByte4, ConvertByte3, ConvertByte2, ConvertByte1;
    ConvertByte4 = (Byte4 << 24) & 0xFF000000;
    ConvertByte3 = (Byte3 << 16) & 0x00FF0000;
    ConvertByte2 = (Byte2 << 8)  & 0x0000FF00;
    ConvertByte1 =  Byte1       & 0x000000FF;

    BigEndianBlock = ConvertByte4 + ConvertByte3 + ConvertByte2 + ConvertByte1;
    return BigEndianBlock;
}
uint32_t LittleEndianToBigEndian_unsignedchar (uint8_t Byte1, uint8_t Byte2, uint8_t Byte3, uint8_t Byte4)
{
    static uint32_t BigEndianBlock, ConvertByte4, ConvertByte3, ConvertByte2, ConvertByte1;
    ConvertByte4 = Byte4 << 24;
    ConvertByte3 = Byte3 << 16;
    ConvertByte2 = Byte2 << 8;
    ConvertByte1 = Byte1;

    BigEndianBlock = ConvertByte4 + ConvertByte3 + ConvertByte2 + ConvertByte1;
    return BigEndianBlock;
}
 

Attachments

  • McDirge_v0.3.zip
    929.5 KB · Views: 12
One thing led to another and experimenting with cfg000 helped me partially unlock the chat.



By pressing R1 on the controller you can get the symbol table on the screen. This table only shows up in the original Japanese release though. I do not know the reason behind why the game can't retrieve Japanese symbols for the actual chat input.

4YLdAoH.png



The actions for "Open Main Menu" and "Map" use a particular table that tells the game which menu each will take you to. Typically, pressing Triangle will take you to the Main Menu and pressing Directional Button Up will instantly take you to the Map. Testing out alternate values I found the following:


[Hex value] - [Menu]
0x00 - [No Menu Pop-up]
0x01 - Main Menu
0x02 - Main Menu
0x03 - Main Menu
0x04 - Main Menu
0x05 - Item
0x06 - [No Menu Pop-up]
0x07 - Customize
0x08 - Map
0x09 - Config, Page 1
0x0A - Chat Input
0x0B - [No Menu Pop-up]
0x0C - Status
0x0D - [No Menu Pop-up]
0x0E - Unidentified/Unknown
0x0F - [No Menu Pop-up]
0x10 - Pause

The two surprises were the Chat Input and the mysterious unidentified/unknown menu. In its complete form it is likely intended for the multiplayer. But right now it is incomplete and only gives you the option to return to the title screen.

fxNEdKO.png


lQV3IWq.png

Interestingly, the buttons that are ACTUALLY devoted to Chat Input (and Chat Icons/Macros or "Battle Emotes") in the data do not draw from any table. I tested out hex values 0-10 and not once could these switches be made to summon menus. There is either an additional address in these button regions that need to have their values changed or the pointers on the actual game, not just the save data, need to be adjusted (similar to how Cure magic and parabolic-Fire was unlocked).
 
When looking at Lucrecia Crescent's profile on the WRO computer, one would assume that the final category reads "Gender" or "Sex".

azbeUjW.png


Extracting the texture reveals that it actually says "Address".
I1aOrup.png


57iJTeE.png

I have never encountered gender/sex categorized under "Address" so I'm going to assume that this was an oversight.

Here is the texture with Lucrecia's profile picture.
1I3lvwa.png


I dream of a world where all the background text of Dirge is readable, even if only in its extracted format. Too often it's impossible to tell for certain what the text is saying.

A0uNjI6.png

A lot of this looks like common letters in cursive or the greek alphabet.



The Turks Tactical Training Facility has this repeating error in its final area where the ground reads "Do not proceed beyond this lin area". It's quite awkward.

Hb9ydUH.png


Here is the extracted texture.

Imwrwo4.png


Yup, the texture itself is correct, it's just pasted wrong in the environment. Most places in the tutorial does in fact paste it correctly.

0dWbubr.png




Equipment graphics for the menus are so beautiful.

iDUZ7j8.png
 
Last edited:
Continuing from the FGC thread, I was curious if I could find out more about the unused materia models and the yellow materia among the Protomateria concept art.

gsdx_20200805215458-png.7482


g006 (string referenced in the concept art)
g006-png.7483


g007 (roughly half the width of the g006 model)
g007-png.7484


The final game never renders the Protomateria as a normal 3D object but instead opts to use the game's particle engine to render the graphics of the Protomateria. Hence why g006, the original Protomateria design, becomes unused. g006 is referenced in the game's memory when you roam Chapter 9 but after extensive testing I haven't actually found the model rendered anywhere.


I contacted @murfy, who is very good at figuring out the kanji of even low-resolution Japanese text. There may be a platform for debate as to whether there are other good kanji candidates, but murfy's deductions seem reasonable. I am very thankful to murfy for their quick response! ^_^

gsdx_20200805215458_murfy-transcription.png

g006 エンシェントマテリア
"g006: Ancient Materia [Protomateria in the English game]"​

イメージ
"Image"​

緊急時
"In emergency"​

安定時
"In stable state"​

通常のマテリア
"Normal materia"​


The reasonable conclusion seems to be that g007 represents a normal materia, perhaps even a generic Command materia. Both the concept art and the in-game counterpart g007 shows this materia with half the radius of the Protomateria, so perhaps all it is here for is a size comparison. All my speculations about materia fusion and graphical effects were way off-base.

There is at least one moment when the Protomateria kinda-sorta flares up in the final game, when Vincent-Chaos stands face to face with Omega, so that's an example of the Protomateria transitioning from a "stable state" to a state of "emergency". Though in all honesty the final game has the Protomateria remain highly flashy at most points, pretty much never giving that "stable state" vibe from the concept art.

Let's look at the pre-release screenshot of a Deepground soldier holding the Protomateria outside of Shinra Manor and compare it with the finalized "Rosso version".


pre-release-version_Protomateria_size-doubled.png

gsdx_20200822165644_smaller.png

The Protomateria never flares up with a flowy pillar of energy like in the screenshot from the concept art. I imagine that if the Protomateria had been shown to radiate as a "state of emergency" throughout the game it would have almost given the Protomateria a consciousness of its own. Keeping the radiance of the Ancient Materia neutral throughout feels more appropriate in my book.

The only Deepground soldier that show hint of sleeves like these is the DG Commander but in the final design the sleeves are covered up by long gloves. It's fun to imagine what the pre-release screenshot might actually mean. Clearly it's part of an unused Deepground soldier design, but was it ever part of the story drafts that a common DG soldier, and not a Tsviet, would pick up the Protomateria at this point in the story? Or was it always meant to be Rosso, only that Rosso's design had not been finalized when this pre-release image was created? A very crazy thought is to imagine Rosso sporting these plain sleeves rather than her metal gloves. :wacky:

There's no way to mimic the camera shot of the early-production image and the final game to fit 1:1. But we can at least discern that little-or-nothing was changed for these exterior shots of Shinra Manor when comparing the screenshots.
 
Last edited:

Cat on Mars

Actually not a cat
The reasonable conclusion seems to be that g007 represents a normal materia, perhaps even a generic Command materia. Both the concept art and the in-game counterpart g007 shows this materia with half the radius of the Protomateria, so perhaps all it is here for is a size comparison. All my speculations about materia fusion and graphical effects were way off-base.
And so were mine.
:femrage:
 
Yesterday was the eleventh day of the eleventh month. Shortly after the eleventh minute of the eleventh hour had struck, I resumed playing Chapter 11 of Dirge of Cerberus for my All S Ranks run for the first time since May. Fortunately it didn't take long for my tactical memory to kick in and I'm feeling pretty good about re-learning Ch11-1. I had actually pretty much mastered Ch11-1 in May but never cleared it because quite frankly I was out of juice after the behemoth that was Chapter 10.

Ch11-1 is really short and if I play for just a few days I'm guaranteed to get it done in my satisfactory, over-the-top fashion. The real challenge will be Ch11-2 because it is lengthy and contains many segments that require both skill and luck to be cleared in a way that looks good. After that it's mostly a series of boss battles that will definitely test my patience but at least there won't be as much choreography to memorize.

As for S rank categories the most difficult one here is Critical Hits and I'll admit up front that I'm going to give myself leeway by cheesing critical hits with the help of a weak weapon. Ch11 is one of those chapters where you suffer most from using overpowered weapons that one-shot enemies to death because it means you get fewer chances at critical hits. In Chapter 6 I used the Model Gun + Short Barrel on the underside of the Black Widow to cheese critical hits. In Chapter 11 there is one Bizarre Bug in particular that I'll be using the Blast Machine Gun on (recall that this machine gun knocks over enemies with each hit) to farm easy strikes on the bug's weak spot.

Yes, I am aware of how arbitrary and inconsistent I am in regards to what desperately needs to be a performance based on skill and perseverance versus what is an okay cheap method to use. :monster:


One part of Ch11-1 that always triggered my inner conspiracy gamer is this spot with 9 item cases and a Mako Point. Each item case contains 100 gil.

Min fantastiska inspelning Skärmavbild 2020-11-12 13-11-40.png

For the longest time I thought to myself "This HAS to be a puzzle!"
Maybe something will happen if you pick up the item cases in the correct order? Do the item cases form a pattern of some kind if you draw lines between them? Are you rewarded for NOT being greedy and not grabbing any of the item cases?

While I can't disprove the existence of a needlessly obscure puzzle, the DC Complete Guide makes no reference to a hidden puzzle. When I stop overthinking the answer becomes simple though: The level designer probably wanted to lure you into a false sense of security.

Just as you are about to step on the Mako Point, several Bizarre Bugs will fall down from the duct above. The EM barricade to the next area will activate and now you need to defeat the Bizarre Bugs in order to find the cardkey and advance in this stage.

By including these suspicious item cases and having NOTHING bad happen as you pick them up, I speculate that the level designer wanted to enhance the jump scare of when the Bizarre Bugs fall down on the Mako Point.
EDIT: The argument could of course be made that picking up the item bags one by one is also supposed to build suspense rather than diminish it before one gets to the Mako Point and the surprise Bizarre Bugs finally appear. :wacky:

Whether the intention is effective or comes across at all is a matter of debate but it's the most reasonable explanation I can come up with as to why these 9 item cases, with such low amounts of gil, are scattered here.


The only thing that the DC Complete Guide appears to say about this area is what happens when you collect the mako. Note however that it is actually possible to trigger the Bizarre Bugs without touching the Mako Point, all you need to do is touch the immediate area around the glowing circle.

POINT 07
魔晄を回収しハシゴを登ろう​
E-7にある魔晄を回収すると真上にあるダクトから​
怪奇虫が落下してくるが、気にせず回収しよう。魔晄回​
収後、すぐにH-8にあるハシゴを上れば、怪奇虫はそ​
こまで追いかけてこない。ハシゴを上りきり、上の段​
から射撃すれば一方的に倒せるのだ。ここで怪奇虫を​
全滅させると、最後に倒した敵がカードキーを落とす​
ので、E-5にあるバリケードを解除しよう。​

[GOOGLE TRANSLATE]
POINT 07
Collect the mako and climb the ladder​
If you collect the mako on E-7, Bizarre Bugs will fall from the duct directly above,​
but let's collect the mako without worrying about it. If you climb the ladder at H-8​
immediately after collecting the mako, the monsters will not chase that far.​
You can unilaterally defeat them by climbing up the ladder and shooting from the top.​
If you annihilate the monsters here, the last enemy you defeated will drop the cardkey,​
so let's unlock the barricade on E-5.​

Because the Bizarre Bugs are so easy to defeat I would have never considered using the shorter ladder to escape to a safe vantage point. If you are underpowered and lack skill then either of the ladders are definitely good retreats though. I appreciate it whenever the official guide makes me look at alternate approaches even if they are 100% not at all relevant to my playthrough.
 
Last edited:

Makoeyes987

Listen closely, there is meaning in my words.
AKA
Smooth Criminal
I love how Bizarre Bugs are the equivalent of roaches in the FFVII world, lurking everywhere and being a nuisance in high numbers...

Especially since they're cuter and don't make my skin crawl upon sight. :monster:
 

Makoeyes987

Listen closely, there is meaning in my words.
AKA
Smooth Criminal
You know what would be funny?

What if Wutai spies brought Bizarre Bugs to the city of Midgar at the beginning of the Wutai-Shinra War as an elaborate scheme of sabotage Shinra that simply failed and introduced a new pest species to the continent?

Hence their name being "bizarre bugs."

:monster:
 
Didn't take me long to reach the same old black hole. A few hours ago I cleared Ch11-1 in a manner that is technically good enough. The only downside is that I didn't extend a killchain and I could have gotten a few more critical hits. There's no problem with my scores for S ranks, the results are good enough.

So naturally I keep playing Ch11-1 for hours after that just in case I can get the PLUS ULTRA result I know I *can* get, it's just that there is a lot of suffering before I can get there. If I upload the current Ch11-1 result I know that I will regret it even though it looks really good, minus that mistake in the middle. Even after all these years I still regret those chapters where I decided to "relax" and not torment myself for perfection. Doesn't matter if I sometimes let myself be sloppy out of health reasons, I still dislike those recordings. :wacky:

Some things never change. ...Including the botched attempts that happen because the left shoulder buttons on my PS2 controller decide to jam. :monster: I did acquire new PS2 controllers but they turned out even more broken than the current one that I've been using since this All S Rank run started.

But the rage, man. The rage. It's so visceral.


*UPDATE: Ch11-1 cleared to my satisfaction! Some awkward stuff did happen but in the end I got a record number of critical hits in this section (so that not everything relies on the cheesing method) and I got to show off the chains I desired. My total killchain count ended up lower than usual but for Ch11 it doesn't matter. The requirement for S rank in killchains is to have at least 35...and I already have 38 (wherein the maximum I would get in this section was 40). I'll take my added number of critical hits, and having secured a 100% accuracy rate, over two killchains. Oh! I also cleared the segment with zero damage. So that's nice.

Not counting the time that I spent strategizing and practicing back in May, I must have spent roughly 10 hours of effective playtime these past three days on Ch11-1. All that work for a segment that is only roughly 4 minutes of performance-relevant gameplay. The final 3 minutes beyond that are only spent backtracking in order to re-collect Mako Points. :awesomonster:
 
Last edited:
THANK YOU SO MUCH FOR THE LOVE @cold_spirit <3 <3 <3

In a similar vein, here is "actual footage" of me preparing equipment and strategies for segments in a run. :awesome:



Ch11-1 and Ch11-2 are fickle in multiple ways. First there are the railings placed all around that have a tendency to absorb your shots, bullets and magic alike. Often times the railings will stop your bullets even when, visually, your line of sight is perfectly clear. This annoyance is one of the prime reasons that Ch11 feels so awkward on casual playthroughs: You want to just play the game and demonstrate everything you've learned thus far but the railings so often stand in your way and foil your strategies. The same problem can manifest in Ch4 (WRO HQ) and in one especially egregious spot in Ch10 (this railing is a prime example of your bullets being blocked even when your line of sight is free, so all you can do is memorize the proper positions and angles that DO work) but Ch11 is the worst offender by far.

Second there are the unpredictable drum can puzzles. The intention is in multiple spots to destroy a cargo crate so that an adjacent drum can fall into position where it might be used to defeat the Deepground Elite. But the angle and position of your shot on the crate will affect the trajectory that the drum can follows when it descends into place. In a casual playthrough where you don't have the benefit of deep memorization, a drum can may fall to a spot where it is obscured (thus rendered useless) or in the worst case it may even fall down into the bottomless pit. The game teases you in how it presents obvious drum can strategies that become almost impossible to execute.

In my case I had to consider not only the above factors but I had to make it work by using magic, since normal bullets on the cargo crates and drum cans would mean a decrease in accuracy rate. The Fire spell always had a hit box that was too wide, meaning that the cargo crate and adjacent drum cans would be destroyed simultaneously, thus removing the explosive with which to defeat the Deepground Elite. Blizzard shots became the solution but even then I had/have to be careful.

cargo-crate_drum-can.png

When using Blizzard on the first crate it is necessary that the magic shot lands on the left half of the cargo crate. If it lands on the right half then the explosion radius will reach the drum can and trigger it to explode. In a later spot I had to take care to shoot the lower half of a cargo crate, lest I accidentally trigger the drum can sitting on top. Once this precision shooting is done, the next step is to use Fire on the drum cans...and discover the dozens of angles and positions from which the railings might absorb your Fire spell and ruin your plans. :lol:

The process of learning how to play Ch11 is definitely rewarding but I do deem much of it to be bad game design. The game actively works against you and not in a way that I would consider fair. The railings shouldn't have to be such huge obstacles (especially when your visual line of sight is free) and the fall trajectories of the drum cans should be fixed, not dynamic due to where- and how your shot lands. Ch10 has multiple drum cans with a fixed fall trajectory and I'm so thankful for that.


Lastly for this post, I simply MUST share an amusing Ch11-2 strategy that I just discovered purely by accident. I deeply wish that I could say it was the result of logical, smart thinking but that is not the case. :mon:

There is one Heavy Armored Soldier B (HASB) that I want to use for farming a high killchain by deflecting its missiles. Problem is that the HASB will unpredictably go on the retreat- or the offense, making it near impossible to consistently melee-deflect missiles.

The solution? Melee the HASB so they are captured in a tight spot where they can no longer move around!

Well, this won't do.
hasb-01.png

GET INTO THE CORNER!
hasb-02.png

Now stay there and think about what you did!
hasb-03.png

I giggle at this amusing, cheap method that allows me to safely melee-deflect missiles and rank up a killchain that allows me to quickly defeat the three Deepground Elite that show up in the next area.
 
- Ch11-2 begins with a Stage Mission where your goal is to defeat the 8 targets around a pillar: 4 DG Elite and 4 Heavy Armored Soldier Bs.
On top of the pillar are 7 drum cans and on top of that 2 cargo crates. Destroy either of the two cargo crates and the drum cans will fall down onto the circular platform where the enemies reside, giving you the means to defeat the targets with relative ease.

gsdx_7-drum-cans_B.png


gsdx_7-drum-cans.png

The problem is that many drum cans will fall down into the void. While using save states to try and optimize the total number of drum cans that fall onto the platform...I was shocked to find that their fall trajectories are highly randomized. Even when shooting a cargo crate from the exact same position and angle, the game randomizes which drum cans end up on the platform instead of the void.

I posited in my previous post that things like the angle of your shot and the position of Vincent is what primarily affects the fall trajectory of drum cans. This certainly appeared to be the case with the drum cans in Ch11-1, where I could reliably replicate results both in console gameplay and when using save states on emulator. But these 7 drum cans that mark the beginning of Ch11-2 are plain and simple just a roll of the dice.

Usually the result is anything from 3 to 6 drum cans that land on the platform. It is possible that the scenario of all 7 drum cans or even fewer than 3 explosive barrels can happen. I was hoping that I could perhaps compensate for some randomness by firing at drum cans in mid-air, before they risk being lost to the void, but this always caused too few kills around the pillar and triggered way too many drum cans that could have been used more effectively later on.

Meaning...I can't even "deep memorize" this section like I did with the drum cans in Ch11-1. I might learn some patterns that are more likely than others to happen but basically I will be reacting to each unique, individual run attempt as they occur and hope that the end result is in my favor. I do not approve.


- Bizarre Bugs...just continue being bizarre and showing off buggy behavior. Except in this case what I once considered a bug/glitch may in fact be an intended feature.

gsdx_20201115192745.png

If you disturb either of these two bugs using a weak shot or melee, two Bizarre Bugs will fall down. Defeating them is worth it not just because of the increase in "Targets Destroyed" but because of the gil they can drop.
Drop table:
A) Nothing
B) 700 gil
C) 1000 gil
D) 5000 gil

Although it is extremely unlikely to happen, it *is* possible for both of the Bizarre Bugs to drop 5000 gil thus netting you a 10000 gil reward.

So far so good. Now, let's reset to before the bugs have fallen down from the air duct. If you insta-kill either of the two bugs, the bugs won't count as "disturbed" and the remaining bug will not fall down. Indeed, you can insta-kill both bugs and the end result is that no bugs fall down. However, insta-killing a bug means that they will NEVER drop any gil. The Bizarre Bugs will only drop gil if they have already landed safely on the ground first.

The behavior that I long considered to be solely a glitch is that if you insta-kill one bug but then disturb the other with a light tap, TWO Bizarre Bugs will fall down from the air duct! You are rewarded with a total of THREE Bizarre Bug kills, even though visually there were only ever two of them.

This is weird but good. Insta-kill one Bizarre Bug, increasing your targets destroyed count by one, then disturb the remaining bug to make two more bugs drop down, defeat them and you've defeated three targets in total and hopefully gained some gil along the way.

I was convinced that when investigating the bit flags that mark when a given enemy has been defeated I would see the game re-use the same flag for an insta-killed bug and one of the bugs that later drop down. I mean, visually there are only two Bizarre Bugs present at any given point so surely that was the intent.

Wrong. There are four unique Bizarre Bugs here, each with their own bit flag. Though you are limited to defeating a maximum of three out of four bugs.

Offset 0x35A8 from scene008 (memory card save data) contains the bit flags for these bugs. A value of 1 means that the bug has been defeated.

[0x35A8] = 00010000 (Right bug if insta-killed)
[0x35A8] = 00100000 (Right bug if killed after falling down from air duct)

[0x35A8] = 01000000 (Left bug if insta-killed)
[0x35A8] = 10000000 (Left bug if killed after falling down from air duct)

For comparison, each of the four Deepground Elite that continuously spawn in the room with the final G Report only use one bit flag respectively no matter how many times they are defeated. So the first time you defeat the DG Elite that spawns in the northeast, the 7th bit in offset 0x35A3 will be set to 1. No new bit flags will be set when that same DG Elite respawn and you defeat them again.

I mention this to emphasize that respawning enemies don't occupy new bit flags for each instance of defeat. So even though the right- or left Bizarre Bug may appear to be a glitchy re-spawn, the bit flag seems to assign them as unique enemies. You may be seeing only two Bizarre Bugs but there are, in fact, four.

there-are-four-bugs.jpg


None of this research impacts my strategies for Ch11-2 but it does change how I view this particular instance of odd behavior that Dirge of Cerberus exhibits. Speaking of odd behavior...


- Earlier this year I went on at length about how the room with the G Report only spawns a maximum of 3 DG Elite soldiers when it is in fact supposed to spawn 4 soldiers. Well...now whenever I enter the room ALL 4 SOLDIERS spawn. Even when using old memory card saves I am usually unable to replicate the glitch where only 3 DG Elite are summoned, which during my February research runs was the norm. On emulator I did somehow manage to make the room change its mind so that first all four soldiers spawned, then only three and then lastly only two BUT I HAVE NO IDEA HOW IT HAPPENED. It's like the game mistook itself from being Hard mode (4 soldiers) then to being on Normal mode (3 soldiers) and then Easy mode (2 soldiers).

I was hoping to find, at least in my old memory card saves, that the reason for the "only-ever-3-DG-Elite" glitch was because the bit flag for one DG Elite had already been set but I'm finding no signs of this being the case. Here are the offsets though (as observed in file scene008) and their bit flags for the four soldiers.
[0x35A3] = 10000000
[0x35A4] = 00000111


g-room.png

It is possible that the glitch I've experienced is limited to emulator. Can't honestly remember if I ever encountered this glitch on console but who knows maybe I'll see it while grinding Ch11-2.


I was at least able to confirm one bit of behavior. So, Ch11-2 ends almost immediately after leaving the G Report room and entering the area with the Dragonfly PT boss. This boss fight begins Ch11-3. After the boss fight you can retreat back to the G Report room and find that previously spawned DG Elite are still in there waiting for you. HOWEVER! It vexed me that sometimes when leaving the game and then reloading Ch11-3 that a few or none of the DG Elite soldiers would ever respawn.

The reason in hindsight is obvious: If the bit flag for a given DG Elite is set then the game will recognize this as a sign that the DG Elite should never spawn again, so long as you load the game from Ch11-3. If you play continuously from Ch11-2 and into Ch11-3, the game will not update itself properly and the DG Elite will continue to respawn endlessly. But if you want a safe haven upon reloading Ch11-3, all you have to do is defeat each unique DG Elite soldier at least ONCE (thus setting the bit flags) during Ch11-2.

Assuming you didn't collect the G Report earlier then it will actually be safer to grab the report after reloading Ch11-3 with a version of the room where no more enemies appear. You're normally a sitting duck when you stand on the rising pillar in the center, waiting for it to rise high enough that you can jump and grab the G Report. Haven't decided yet if I will go for this strategy or not, though going for this method would definitely diminish the amount of total damage I receive during Chapter 11.

...I say this but I have a strong memory of encountering a glitch(?) where the central pillar in the G Report room never rises again when you revisit it in Ch11-3. Can't find the save file now when this occurred. A false memory? If it is, it's sure one heck of a vivid false memory.
 
Last edited:
Confirmed that the glitch with not all 4 DG Elite spawning in the G Report room is very much a thing that can happen on a real console as well! I do not have a recording from when I first entered the room but the scenario started with either 3 or 4 of the DG Elite spawning. Defeated 2 of them, possibly 3, then after transforming into Galian Beast...the room only ever spawned a DG Elite from the northeast gate.

Defeated this DG Elite a few times while in Vincent form (using magic), then I defeated the Bizarre Bug to the south...and then there was nothing. No DG Elite ever spawned while I was still in that room. I retreated back into an earlier part of the stage and made sure to move past an obvious loading zone before returning to the G Report room. When I did...all 4 DG Elite had spawned again. They didn't even wait to respawn until I had re-entered the room.

EDIT: Now I got it to happen again, except this time it was only the southeast DG Elite that kept spawning and I could never trigger that spawning loop to quit. This time I left the room on the northern side (approaching the Dragonfly PT boss battle) and returned, upon which all 4 DG Elite were now instantly present again. I didn't even cross a loading zone that caused the disc-icon to appear in the upper right corner of the screen. Clearly leaving the room in either direction, east or west, is enough to reset it.

EDIT #2: Didn't even have to leave the room to reset. One time all I had to do was stand next to the exit and then suddenly two DG Elite that hadn't appeared in several minutes could now spawn again. Been able to trigger so many variations now of this glitch with different DG Elite being locked away and then gradually lowering how many are able to appear at all.


This is wild. None of the difficulty modes have a scenario where only one DG Elite ever spawn, let alone zero, which seems to disprove the speculation that the game is accidentally shifting between difficulty modes. Rather, the problem has something to do with the normal spawning loops being interrupted.

Graphically the room also has this glitch where, sometimes, a gate will not close even after a DG Elite has walked out from the gate. When it is time for a new DG Elite to appear however, the open gate becomes closed in the next frame and then opens accordingly. This minor graphical bug may or may not have anything to do with the room's tendency to interrupt the spawning loop of the DG Elite.

The room has a tendency to lose frames, probably due to the game being overwhelmed by all the particle effects of the rising gases. Ch11 has a handful of these spots where you'll be losing frames but it typically happens while the game is also dealing with drum can explosions.

I should note that the G Report room also has a tendency to cause PCSX2 to freeze.

This YouTube video from 2008 also shows a player of the post-JORG version falling through the central pillar and then being stuck inside that circle even though they are able to jump back to a normal elevation. Notably the pillar also never decides to start rising again in this clip.


This room in particular is...fucky.
 
Last edited:
Praying that my voltage converter doesn't explode any time soon. It was particularly noisy today and without it I can't play Dirge on console. I know from experience that voltage converters typically don't last more than a few years at most. If anybody knows a brand that is reliable for longer periods, please let me know.


Ch11-2 is turning into a project the size of the more difficult portions of Chapter 10. Having fun while learning ever more about this segment even though I'm realizing that the intricacy of Ch11-2 will require me to play potentially dozens of hours more before I got all the strategies pinned down.

Understanding the AI of enemies in the first area, stage mission "Defeat all targets on the central pillar!", is satisfying.

Ch11-2_area-1_objects.png

- Red Saucers will only spawn if you are standing on any of the four circular platforms. Stand on either of the two southern platforms and Red Saucers will appear from the southern spawn point. Stand on either of the two northern platforms and Red Saucers will appear from the northern spawn point. On Hard mode a maximum of two Red Saucers can be present at any time. Once a defeated Red Saucer has faded away, a new one can be spawned again.

- The Heavy Armored Soldier B (HASB) are programmed to fire missiles instantly when Vincent gets in their line of sight. If Vincent stays visible to the enemy, there is then the usual 4-5 second countdown before a HASB fires again. But if Vincent moves out of sight and then back in again, a missile is fired regardless of how little time passed since the last missile was fired.

In addition to this, so long as Vincent is visible to the enemy, missiles are automatically fired whenever you cross the invisible lines that mark the edge of a given circular platform.

All the above explains why the missile barrage from the HASBs has such high frequency. Understanding this behavior makes it a bit less overwhelming since now it's predictable.

- While the DG Elite will only fire shots so long as Vincent is visible, they don't seem to be influenced by the same factors as the HASB enemies.

The randomness of this first room (see my earlier post about the randomness of the drum cans) will sometimes benefit a run and other times severely damage it. 2-3 times I've actually managed to get RNG so good that I am able to defeat ALL 8 targets around the pillar before even stepping onto the first circular platform! Other times the RNG is so bad that I'm forced to directly use magic on a DG Elite because no drum can landed close enough earlier to help in the demise of that DG Elite. This bad RNG usually leads to bad results as I have to walk into enemy fire, have my killchain undone by shots from a DG Elite and thus require multiple magic spells to kill this last DG Elite.


The stage mission "Defeat the gargoyles! 3" (9 gargoyles) feels a bit like the gargoyles in Ch10-1 and I'm starting to get an idea of how to best approach this mission. There are still hours of attempts left though before I can become confident in this section.


Getting better and better at making the HASB in the subsequent room get trapped in a proper corner. This enemy is a prime example of weird enemy AI that I doubt was intended. If the HASB has fallen to the ground and the player starts using melee on the enemy, the animation where the enemy lifts itself back to its feet is interrupted. Instead the HASB becomes instantly upright again and fires a missile. It does appear that when a HASB is touched/melee'd then it will fire a missile, regardless of where the enemy is in its normal cycle of firing missiles every 5-6 seconds.

I've encountered this behavior with other HASBs that they are more likely to fire missiles if the player is using melee. It is quite likely that if I hadn't landed those two first weak melee strikes on this HASB in Ch10-2 then the enemy wouldn't have shot me with a missile at that moment. That's why it's so important that the only melee you land is the strike (third and/or fourth) that actually knocks the enemy back. This way the HASB won't have time to fire a missile that'd otherwise be triggered by melee/touching.


Then there's the G Report room...where I end up wasting so much time playing around and triggering glitches.

- The recording of this was lost but I somehow triggered a glitch where the northeast DG Elite was frozen aiming down each time it left the gate. The enemy never fired any shots and didn't react to Vincent. Leaving the room undid the glitch.

- One time a DG Elite zig-zagged halfway across the room and back while it fell down from one of the pipes. This might just be another variation of the general bug where enemies suddenly fly across the stage. My theory is that something causes the enemy to latch onto inputs from your right analog stick and that those inputs are translated into movement.

- Without any interruption from me as the player, one DG Elite got stuck behind its own gate and the gate refused to open. They could still shoot at me and even land hits, but I could not hurt them (even though Fire magic went through the gates and registered hits). This problem didn't reset even by backtracking far through the stage.

Apart from me shilly-shallying in this G(litch) Report room, there's also the matter that the choreography I have planned for this room is ridiculously dependant on luck. The goal is to defeat each DG Elite at least once, defeat both the Bizarre Bugs and then leave. If I pull this off the way that I've done on emulator, I can leave the room with zero damage and possibly even leave with a high killchain. The G Report will then be retrieved in Ch11-3, as there is no way to grab the G Report in Ch11-2 without taking damage.

On console I haven't even come close to pulling off this stunt. Even for me this plan is ridiculously flashy and pointless. The reason I'm invested in this zero-damage battle plan for the G Report room is because the DG Elite are so overpowered in this room and I just want to STICK IT TO THEM!

Doesn't help of course that sometimes the glitch is instantly triggered where some DG Elite don't spawn (the south- and northwest DG Elite are especially prone to this), which renders the run moot seeing as I want to defeat each DG Elite at least ONCE so that they may not respawn when I load Ch11-3.
 
Last edited:
I took a break yesterday from Dirge of Cerberus to hopefully slow down the inevitable advent of insanity that Ch11-2 is bringing. After an hour of attempts today I can sense myself losing faith.

Half the time I can't even enter the G Report room with a proper killchain because something outside of my control messed up the killing of the Bizarre Bugs and/or the DG Elite before that room. That renders the run failed seeing as I need a high killchain in order to one-shot two DG Elite with help of Thunder Lv3 upon first entering the G Report room.

Few of the times when I enter the G Report room actually go well and when they do, the run is undone because one or more DG Elite refuse to spawn. For a while I thought I had circumvented the glitch by pausing the game at particular intervals (thus, theoretically, causing the game to reset itself to an extent) but it turns out that doesn't actually work. Only ONCE have I made it HALFWAY through my intended choreography of the G Report room. That's the best result yet.

Also disheartening is that even now when I know exactly how to clear the gargoyles stage mission, I am yet to clear it with 100% accuracy rate. One of the big crimes of Dirge is that sometimes an enemy's hit box is defined as where the enemy is going to be and other times it is defined as the enemy's current position. With large targets this of course isn't much of a problem. I have learned exactly how misplaced the actual hit box of the Dragonfly PT boss in Ch11-3 is, meaning I know exactly where in empty space to shoot (adjacent to the boss) in order to *actually* land that critical hit on the helicopter's nose. And no, this isn't a case of bullets taking really long to traverse the stage and thus landing "where the enemy is going to be": This is straight up the hit box placement being flawed.

The gargoyle hit boxes are particularly unpredictable and chaotic. The gargoyles in Ch10-1 were way easier because for some reason you almost never had to worry about their half-invisible spit shots landing on you and you could wait until they were stationary. Here in Ch11-2 the gargoyles' spit shots have accurate homing, necessitating that you kill the gargoyles while they are still flying through the air. Upping the difficulty so that you must destroy targets in motion makes sense and I would be way more forgiving of this if only the hit boxes weren't so buggy.

Despair is also generated if I get several attempts of bad RNG with the drum cans in the first part of Ch11-2.

And guess what? I haven't even begun recording whole sessions yet. I'm still in the "casual trial phase". I just don't have any proof yet that I can perform Ch11-2 in the way that I desire. I might never get that proof if only because of how glitchy the G Report room is. ...I should look into if I might change my planned choreography for the room so that I always brush by the exits and thus reset the glitchy gateways just in case. =/


Pointless observation I've been wanting to make: Hera from Blood of Zeus looks like Rosso the Crimson.
blood-of-zeus_Hera_notRosso.png
 
Last edited:
The first room in Ch11-2 is not nearly as buggy as the other rooms but I still managed to trigger one glitch somehow: Red Saucer got stuck in the pipe from which it spawns.



In all my runs there is one glitch that people commonly run across but that I've never triggered: The one where a cardkey spawns out of bounds, thus soft-locking your session. The DG Elite who drops the cardkey has 3500 HP, in contrast with the usual 1000 HP that DG Elite has in Chapter 11.
https://www.twitch.tv/sk84uhlivin/video/48519053

I speculate this is more likely to happen if you are using melee to finish this DG Elite. My strategy always involves landing headshots on the DG Elite until they die and not once has the above glitch happened for me. Speedrunners seem to encounter this glitch quite often and their method is typically to just spam melee. It is also possible of course that maybe this error is less likely to happen in the Japanese Original version, ergo why I haven't personally triggered it yet.



This may have only happened to me once (possibly twice) in all my runs that all the DG Elite stop spawning after their first defeat. If this happened to me in a recorded god run I might actually be fine with it because it's such a ridiculously optimized event; nothing to stop you from casually picking up the G Report without receiving damage. As you can spot at 2:17, reaching one of the exits will reset the room and suddenly all the DG Elite can spawn again.

One crucial observation that I've made: Tempsaves preserve the error where a gateway remains closed and a DG Elite never emerges. In JORG, Tempsave data exists in the "BISLPM-66271000" file (the screenshot for the Tempsave is saved in the "scr000" file). Meaning that the variables for at least the gateway switches could be pinpointed by comparing Tempsaves where the bug is happening versus Tempsaves where the error has not happened yet.

I HIGHLY doubt that even if I figure out the cause behind the glitch that I'll be able to use that knowledge to aid me. Getting the impression that the glitch occurrence is primarily due to the whims of the game's flawed loading schemes, so finding which switches mark the state of each gateway may be interesting but ultimately unhelpful.
 
Top Bottom