├── Lib
├── CMakeDLL.cmd
├── Icon.ico
├── RemoveGarbageToLookupTable.ahk2
├── StringToCRC32.ahk2
├── MigrateFileToNewMixFile.ahk2
├── InBin.ahk2
├── LMDSelector_ChooseLMD.ahk2
├── remixer.c
├── GetMixInformations.ahk2
└── Class_LV_Colors.ahk2
├── Launcher.ahk2
├── README.md
└── reMIXer.ahk2
/Lib/CMakeDLL.cmd:
--------------------------------------------------------------------------------
1 | gcc -shared -fPIC -O2 -g0 reMIXer.h -o reMIXer.dll
--------------------------------------------------------------------------------
/Launcher.ahk2:
--------------------------------------------------------------------------------
1 | run A_ScriptDir . "\x" . (A_Is64bitOS?"64":"86") . "\reMIXer.exe"
2 | exitapp
--------------------------------------------------------------------------------
/Lib/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldrin-John-Olaer-Manalansan/RA2YR-reMIXer/HEAD/Lib/Icon.ico
--------------------------------------------------------------------------------
/Lib/RemoveGarbageToLookupTable.ahk2:
--------------------------------------------------------------------------------
1 | Key1 := SubStr(Key1,1,InStr(Key1,"(")-1)+0 ; parse string then convert string to integer by adding zero
2 | if LookUpTable.Has(Key1)
3 | LookUpTable.Delete(Key1)
4 | if OverlapMap.Has(Key1)
5 | OverlapMap.Delete(Key1)
6 | For Key2 in OverlapMap
7 | {
8 | if OverlapMap[Key2].Has(Key1)
9 | {
10 | if (OverlapMap[Key2].Count >= 2)
11 | OverlapMap[Key2].Delete(Key1)
12 | else OverlapMap.Delete(Key2)
13 | }
14 | }
--------------------------------------------------------------------------------
/Lib/StringToCRC32.ahk2:
--------------------------------------------------------------------------------
1 | StringToCRC32(str)
2 | {
3 | try
4 | {
5 | buf := Buffer(Strlen(str), 0)
6 | StrPut(str, buf, buf.size, "UTF-8")
7 | vHash := 0xFFFFFFFF & DllCall("ntdll.dll\RtlComputeCrc32", "UInt", 0, "Ptr", buf, "UInt", buf.size)
8 | }
9 | catch
10 | {
11 | vHash := 0xFFFFFFFF
12 | Loop Parse, str
13 | {
14 | vHash := vHash ^ Ord(A_LoopField)
15 | Loop 8
16 | vHash := (vHash & 1) ? (vHash >> 1) ^ 0xEDB88320 : (vHash >> 1)
17 | }
18 | vHash := 0xFFFFFFFF & ~vHash
19 | }
20 | return vHash
21 | }
--------------------------------------------------------------------------------
/Lib/MigrateFileToNewMixFile.ahk2:
--------------------------------------------------------------------------------
1 | isMigrateFileSuccessful := true
2 | try {
3 | DllCall("MSVCRT.dll\memcpy", "Ptr", reMIX_Area_Body_Ptr + reMIX_Area_Body_Size, "Ptr", File_Ptr, "UInt", File_Size, "CDecl Ptr")
4 | } catch as e {
5 | if DllCall("MSVCRT.dll\memcpy_s",
6 | "Ptr" , reMIX_Area_Body_Ptr + reMIX_Area_Body_Size,
7 | "UPtr", reMIX_Area_Body_MaxAllowedSize - reMIX_Area_Body_Size,
8 | "Ptr" , File_Ptr,
9 | "UPtr", File_Size,
10 | "CDecl Int") ; has error
11 | {
12 | msgbox "Invalid memcpy_s parameters:`n`nFile_Path: " . FileName . "`nFile_ID: " . File_ID . "`nDestination: " . Format("0x{:X}", reMIX_Area_Body_Ptr + reMIX_Area_Body_Size) . "`nDestinationSize: " . reMIX_Area_Body_MaxAllowedSize - reMIX_Area_Body_Size . "`nSource: " . Format("0x{:X}", File_Ptr) . "`nSourceSize: " . File_Size . "`nMaxAllowedSize: " . reMIX_Area_Body_MaxAllowedSize . "`n`nPlease Copy this Error message(Press CTRL+C) then create an issue with this information inside at:`nhttps://github.com/Aldrin-John-Olaer-Manalansan/RA2YR-reMIXer/issues"
13 | isMigrateFileSuccessful := false
14 | }
15 | }
16 | NumPut( "UInt", File_ID,
17 | "UInt", reMIX_Area_Body_Size, ; Offset from Body
18 | "UInt", File_Size,
19 | reMIX_Seeker)
20 | reMIX_Seeker += 12 ; next Index containing Information
21 | reMIX_Area_Body_Size += File_Size
--------------------------------------------------------------------------------
/Lib/InBin.ahk2:
--------------------------------------------------------------------------------
1 | InBin(pHaystack, HaystackBytes, pNeedle, NeedleBytes, StartingPos:=1, Occurence:=1)
2 | {
3 | Static mCode
4 | Local mSz
5 |
6 | If not IsSet(mCode)
7 | mSz := A_PtrSize=8 ? 268 : 248
8 | , mcode := Buffer(mSz)
9 | , DllCall("Kernel32\VirtualProtect", "ptr",mcode, "ptr",mSz, "int",0x40, "int*",0)
10 | , DllCall("Crypt32\CryptStringToBinary", "str", A_PtrSize=8
11 | ? "U1ZXQVSLRCRIRItcJFCJ00Qpy4XAvgEAAABBuv////9BD07yhcB+B2dEjVD/6wgBwkGJ0kUpykGD6QFFhdJyQzHSQTnadzxEidBB"
12 | . "ijhAODwBdShEichBigQAZ0ONPAqJ/zgEOXUVQYP5AnMbg8IB6wVEOc909kQ52nRDQQHyRYXSc78xwOs9vwEAAABBg/kBdt8PH0QA"
13 | . "AGYPH4QAAAAAAIn4QYoEAGdFjSQ6RYnkQjgEIXW9g8cBRDnPcuTrs0SJ0EgByEFcX15bwwAARYXAdjoxwEGJwUaKFAlBgPpAdhJB"
14 | . "icNCgDwZW3MIQbsgAAAA6wNFMdtFD7bSRQHaRYjSRogUCoPAAUQ5wHLIww" : "VYnlg+wQU1ZXi1UIi00Qi3UUi0UMKfC"
15 | . "JRfQxwIN9GAAPntD32IPg/kCJRfyDfRgAfgmLRRhIiUX46wuLRQwDRRgp8IlF+ItF+InHToX/cjvHRfAAAAAAO330dy+KBDo6AXU"
16 | . "hjQQ3igQCOgQxdRaD/gJzHP9F8OsEOfN094tF8DtFHHQnA338hf9zzDHA6x+7AQAAAIP+AXbfjQQfigQCOgQZddRDOfNy8OvNjQQ"
17 | . "6X15biexdwwAAAFNWV4tEJBCLVCQUi0wkGIXJdiYx9oocMID7QHYNgDwwW3MHvyAAAADrAjH/D7bbAfuIHDJGOc5y3F9eW8MAAAA"
18 | , "int",A_PtrSize=8 ? 358 : 331, "int",0x1, "ptr",mCode, "int*",mSz, "int",0, "int",0)
19 |
20 | FoundPtr := DllCall(mcode, "Ptr",pHaystack, "Int", HaystackBytes
21 | , "Ptr",pNeedle, "Short", NeedleBytes
22 | , "Int",StartingPos,"Int",Occurence, "CDecl Ptr")
23 |
24 | ErrorLevel := ( FoundPtr="" ? "Memory access violation" : FoundPtr=0 ? "Needle not found." : "" )
25 | Return FoundPtr
26 | }
--------------------------------------------------------------------------------
/Lib/LMDSelector_ChooseLMD.ahk2:
--------------------------------------------------------------------------------
1 | if (LMDFile_OffsetfromBody.Length == 1)
2 | File_StartingOffsetfromBody := LMDFile_OffsetfromBody[1]
3 | else ; two or more conflicting LMD that could possibly be the real one
4 | {
5 | Gui_LMDSelector_FilePath.Value := FileName
6 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Update Listview of the LMD Selector GUI
7 | Gui_LMDSelector_LV.Opt("-Redraw")
8 | Gui_LMDSelector_LV.Delete()
9 | Area_Info_OffsetFromBody_Ptr := Area_Info_Ptr + 0x4
10 | Area_Body_LMD_Size_Ptr := Area_Body_Ptr + 0x20
11 | Area_Body_LMD_NamesCount_Ptr := Area_Body_Ptr + 0x30
12 | Gui_LMDSelector_LV.Focus()
13 | Loop LMDFile_OffsetfromBody.Length
14 | {
15 | Index := A_Index
16 | LookupTableIndex := "Unused"
17 | Loop LMDLookup.Length
18 | {
19 | ReverseIndex := LMDLookup.Length - A_Index + 1
20 | if (LMDFile_OffsetfromBody[Index] == NumGet(Area_Info_OffsetFromBody_Ptr + (LMDLookup[ReverseIndex].Index * 12), "UInt"))
21 | {
22 | LookupTableIndex := (ReverseIndex == LMDLookup.Length)?"Default":"Ignored"
23 | break
24 | }
25 | }
26 |
27 | Gui_LMDSelector_LV.Add((LookupTableIndex=="Default")?"Focus Select Vis":""
28 | ,Format("0x{:X}",LMDFile_OffsetfromBody[Index])
29 | ,NumGet(Area_Body_LMD_Size_Ptr + LMDFile_OffsetfromBody[Index], "UInt")
30 | ,NumGet(Area_Body_LMD_NamesCount_Ptr + LMDFile_OffsetfromBody[Index], "UInt")
31 | ,LookupTableIndex)
32 | }
33 | Gui_LMDSelector_LV.ModifyCol(1, "Integer Sort", "Relative Offset From Body(" . Format("0x{:X}",Area_Body_Offset) . ")")
34 | Loop Gui_LMDSelector_LV.GetCount("Column")
35 | Gui_LMDSelector_LV.ModifyCol(A_Index, "AutoHdr Center")
36 | if !Gui_LMDSelector_LV.GetNext()
37 | Gui_LMDSelector_LV.Modify(LMDSelector_AutoPick(), "Focus Select")
38 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
39 | PublicAPIValue(-1)
40 | Gui_LMDSelector.Show("Center")
41 | Gui_LMDSelector_LV.Opt("+Redraw")
42 | while ((File_StartingOffsetfromBody := PublicAPIValue()) == -1)
43 | sleep 1
44 | Gui_ControlPanel.Show("Center")
45 | ;msgbox LMDFile_OffsetfromBody . " " . Format("0x{:X}", File_StartingOffsetfromBody)
46 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RA2YR reMIXer
2 | ## v3.1.0.0
3 | ------------
4 |
5 |
6 | About
7 |
8 | ***RA2YR reMIXer is a powerful tool designed to reconstruct RA2YR MIX files. We do this in order to:***
9 |
10 | 1) Reduce the file size of the MIX File by removing unused content inside the MIX File.
11 | 2) Reverse/Remove Modern MIX Protection Techniques like:
12 | - Header Corruption resulting XCC Mixer to fail on browsing the MIX File.
13 | - LMD Lookup Table Corruption resulting XCC Mixer to show incorrect file names that are found inside the MIX File.
14 |
15 | RA2YR reMIXer is formerly known as `RA2 MIX Unprotector Master`
16 |
17 | ------------
18 |
19 |
20 | Trivia
21 |
22 | What is a .mix File?
23 | *it is a Compressed File like ".zip/.rar" used by RA2/RA2YR to store all of their crucial files in one place. It is the ART of being ORGANIZED!*
24 |
25 | What is MIX Protection?
26 | *MIX Protection means CORRUPTING the MIX file's HEADER, so that NOOBS trying to open .mix files in order to STEAL the works of other people will have some trouble over it.*
27 |
28 | What is MIX Unprotection?
29 | *MIX Unprotection means RECOVERING the MIX file's HEADER, so that "WhiteHat Hackers" can troubleshoot some problems stored on a MIX File.*
30 |
31 | What is LMD Corruption?
32 | *XCC Mixer uses local mix database.dat(LMD) File to know all the file names that are found inside the MIX. When LMD Informations becomes corrupted, XCC Mixer will fail on recognizing the file name of each file. These Important LMD Informations were:*
33 | *1. LMD LookUp Table - Common MIX Protectors corrupts this by puting 12 bytes of garbage value. But this can be recovered as long as the LMD File itself is present inside the MIX File.*
34 | *2. LMD File itself - Modern MIX Protectors today removes the LMD File entirely inside the MIX File. So there is no way of recovering the File names if this happens.
*
35 |
36 | What is LMD Recovery?
37 | *It means we are restoring the file names one by one from LMD remnants found inside the MIX File, then fix the LMD Lookup Table to let XCC recognize file names once again.*
38 |
39 | ------------
40 |
41 |
42 | Installation
43 |
44 | 1. Download the [Latest Released Build](https://github.com/Aldrin-John-Olaer-Manalansan/RA2YR-reMIXer/releases/download/Latest-Build/RA2YR_reMIXer.zip).
45 | 2. Extract the content of this *ZIP* File anywhere you want.
46 | 3. Run **Launcher.exe** to start reMIXing.
--------------------------------------------------------------------------------
/Lib/remixer.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | enum {
6 | CONFLICTTYPE_NONE,
7 | CONFLICTTYPE_DUPLICATE,
8 | CONFLICTTYPE_1CONTAINS2,
9 | CONFLICTTYPE_2CONTAINS1,
10 | CONFLICTTYPE_OVERLAP
11 | };
12 |
13 | typedef struct {
14 | const uint32_t startingOffsetfromBody;
15 | const uint32_t endingOffsetfromBody;
16 | const uint16_t key;
17 | } __attribute__((packed)) info_t;
18 |
19 | typedef struct {
20 | uint16_t initialIndex1;
21 | uint16_t initialIndex2;
22 | const uint16_t totalIndex;
23 | info_t *buffer;
24 | } __attribute__((packed)) data_t;
25 |
26 | typedef struct {
27 | uint32_t encryption;
28 | uint16_t fileSize;
29 | uint32_t bodySize;
30 | } __attribute__((packed)) header_t;
31 |
32 | typedef struct {
33 | uint32_t id;
34 | uint32_t offsetfrombody;
35 | uint32_t size;
36 | } __attribute__((packed)) lookup_t;
37 |
38 | #define ConflictDetected(CONFLICTTYPE) { \
39 | data->initialIndex2 = index2; \
40 | return CONFLICTTYPE; \
41 | }
42 |
43 | #define ConflictCheck() { \
44 | for (; index2 < totalIndex; index2++) { \
45 | if ((buffer1->startingOffsetfromBody == buffer2->startingOffsetfromBody) && (buffer2->endingOffsetfromBody == buffer1->endingOffsetfromBody)) { \
46 | /* Index2 was a duplicate of Index1 */ \
47 | ConflictDetected(CONFLICTTYPE_DUPLICATE) \
48 | } else if ((buffer1->startingOffsetfromBody <= buffer2->startingOffsetfromBody) && (buffer2->endingOffsetfromBody <= buffer1->endingOffsetfromBody)) { \
49 | /* Index1 was a container of Index2 */ \
50 | ConflictDetected(CONFLICTTYPE_1CONTAINS2) \
51 | } else if ((buffer2->startingOffsetfromBody <= buffer1->startingOffsetfromBody) && (buffer1->endingOffsetfromBody <= buffer2->endingOffsetfromBody)) { \
52 | /* Index2 was a container of Index1 */ \
53 | ConflictDetected(CONFLICTTYPE_2CONTAINS1) \
54 | } else if (((buffer2->startingOffsetfromBody <= buffer1->startingOffsetfromBody) && (buffer1->startingOffsetfromBody < buffer2->endingOffsetfromBody)) \
55 | || ((buffer2->startingOffsetfromBody < buffer1->endingOffsetfromBody ) && (buffer1->endingOffsetfromBody <= buffer2->endingOffsetfromBody)) \
56 | || ((buffer1->startingOffsetfromBody <= buffer2->startingOffsetfromBody) && (buffer2->startingOffsetfromBody < buffer1->endingOffsetfromBody)) \
57 | || ((buffer1->startingOffsetfromBody < buffer2->endingOffsetfromBody ) && (buffer2->endingOffsetfromBody <= buffer1->endingOffsetfromBody))) { \
58 | /* file Overlap was detected, we escalate its garbage level */ \
59 | ConflictDetected(CONFLICTTYPE_OVERLAP) \
60 | } \
61 | buffer2++; /* next index2 */ \
62 | } \
63 | }
64 |
65 | uint8_t FileConflictChecker(data_t *data) {
66 | uint16_t index2, totalIndex;
67 | info_t *buffer, *buffer1, *buffer2;
68 |
69 | // avoid access overhead by saving them inside a local variable
70 | index2 = data->initialIndex2 + 1; // next index
71 | totalIndex = data->totalIndex;
72 | buffer = data->buffer;
73 |
74 | if (index2 >= totalIndex) {
75 | // new iteration
76 | data->initialIndex1++;
77 | index2 = 1;
78 | //
79 | }
80 |
81 | if (data->initialIndex1 < totalIndex-1) {
82 | //
83 | if ((index2 >= 2) && (index2 < totalIndex)) {
84 | buffer1 = &buffer[data->initialIndex1];
85 | buffer2 = &buffer[index2];
86 | ConflictCheck()
87 | data->initialIndex1++;
88 | }
89 |
90 | buffer1 = &buffer[data->initialIndex1];
91 | for (; data->initialIndex1 < totalIndex-1; data->initialIndex1++) {
92 | index2 = data->initialIndex1 + 1;
93 | buffer2 = &buffer[index2];
94 | ConflictCheck()
95 | buffer1++; // next index1
96 | }
97 | }
98 |
99 | data->initialIndex1 = totalIndex;
100 | data->initialIndex2 = 1;
101 | return CONFLICTTYPE_NONE;
102 | }
103 |
104 | uint32_t ReconstructMIX(void *source, void *destination, uint32_t destination_Body_MaxSize, uint16_t *lookupIndexes, uint16_t count) {
105 | lookup_t *source_LookupTable = source + sizeof(header_t);
106 | void *source_Body = (void*)source_LookupTable + ((*((uint16_t*)(source + 4))) * sizeof(lookup_t));
107 |
108 | lookup_t *destination_LookupTable_Seeker = destination + sizeof(header_t); // lookup info of lmd file
109 | uint32_t destination_Body_CurrentSize = destination_LookupTable_Seeker->size; // ptr to the end of the lmd file
110 | if (destination_Body_MaxSize < destination_Body_CurrentSize)
111 | return 0; // error: no more space for the body
112 | uint32_t destination_Body_RemainingSize = destination_Body_MaxSize - destination_Body_CurrentSize;
113 |
114 | destination_LookupTable_Seeker++; // start at destination lookup index 1 since index 0 is the lmd file
115 | void *destination_Body = (void*)destination_LookupTable_Seeker + (count * sizeof(lookup_t)); // count didn't include the lmd file, but the seeker already included it.
116 | void *destination_Body_Seeker = destination_Body + destination_Body_CurrentSize; // start the end of the lmd file
117 |
118 | for (uint16_t i = 0; i < count; i++) {
119 | lookup_t *lookupInfo = &source_LookupTable[lookupIndexes[i]];
120 | if (destination_Body_RemainingSize < lookupInfo->size)
121 | return 0; // error: no more space for the body
122 | destination_LookupTable_Seeker->id = lookupInfo->id;
123 | destination_LookupTable_Seeker->offsetfrombody = destination_Body_CurrentSize; // the offset where this file is located at the body is equal to the current body size
124 | destination_LookupTable_Seeker->size = lookupInfo->size;
125 | memcpy(destination_Body_Seeker, source_Body + lookupInfo->offsetfrombody, lookupInfo->size);
126 | destination_LookupTable_Seeker++; // next index
127 | destination_Body_Seeker += lookupInfo->size; // ptr to the EOF
128 | destination_Body_CurrentSize += lookupInfo->size; // include the file size to the overall body size
129 | destination_Body_RemainingSize -= lookupInfo->size;
130 | }
131 |
132 | ((header_t*)destination)->encryption = 0; // no encryption
133 | ((header_t*)destination)->fileSize = count + 1; // indexedfiles + lmd
134 | ((header_t*)destination)->bodySize = destination_Body_CurrentSize; // destination_Body_CurrentSize = destination_Body_Seeker - destination_Body
135 | return destination_Body_Seeker - destination; // return file size
136 | }
--------------------------------------------------------------------------------
/Lib/GetMixInformations.ahk2:
--------------------------------------------------------------------------------
1 | Files_Count := NumGet(MixFileObj, 0x4,"UShort")
2 |
3 | Area_Info_Offset := 0xA
4 | Area_Info_Ptr := MixFileObj.Ptr + Area_Info_Offset
5 | Area_Info_Size := (Files_Count * 12)
6 | Area_Body_Offset := Area_Info_Size + Area_Info_Offset
7 | Area_Body_Size := MixFileObj.Size - Area_Body_Offset
8 | Area_Body_Ptr := MixFileObj.Ptr+Area_Body_Offset
9 |
10 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Get all Possible LDM_Addresss of the LMD File within the file
11 | LMDFile_OffsetfromBody := []
12 | Seeker := MixFileObj.Ptr+Area_Body_Offset
13 | Seeker_RemainingBytes := Area_Body_Size
14 | UIntBuffer := Buffer(4)
15 | LMD_Header_Pattern := Buffer(32)
16 | NumPut("UInt", 0x20434358,
17 | "UInt", 0x4F207962,
18 | "UInt", 0x2066616C,
19 | "UInt", 0x206E6176,
20 | "UInt", 0x20726564,
21 | "UInt", 0x6B657053,
22 | "UInt", 0x2717041A,
23 | "UInt", 0x00801910,
24 | LMD_Header_Pattern)
25 |
26 | LMDMap := Map()
27 | while (FoundPtr := InBin(Seeker, Seeker_RemainingBytes, LMD_Header_Pattern.Ptr, 32))
28 | {
29 | File_Size := NumGet(FoundPtr+32, "UInt")
30 | File_StartingOffsetfromBody := FoundPtr-Area_Body_Ptr
31 | File_EndingOffsetfromBody := File_StartingOffsetfromBody+File_Size
32 | if (File_Size > 0) and (Area_Body_Size >= File_EndingOffsetfromBody) ; valid info / not corrupted
33 | {
34 | File_Ptr := Area_Body_Ptr + File_StartingOffsetfromBody + 52 ; pointer to start of the name dictionary list
35 | ; msgbox Format("{:X}",File_Ptr)
36 | RemainingSize := File_Size - 52
37 | while (RemainingSize > 0)
38 | {
39 | Name := StrGet(File_Ptr, RemainingSize, "UTF-8")
40 | NameSize := StrLen(Name)
41 | CRC32 := StrUpper(Name)
42 |
43 | ; RA2 Unique step before doing CRC32
44 | if (NameSize & 3)
45 | {
46 | Mask := NameSize & ~3
47 | CRC32 .= Chr(NameSize - Mask)
48 | AppendChar := SubStr(CRC32,Mask + 1,1)
49 | Loop 3 - (NameSize & 3)
50 | CRC32 .= AppendChar
51 | }
52 |
53 | CRC32 := StringToCRC32(CRC32)
54 |
55 | NumPut("UInt", CRC32, UIntBuffer)
56 | if (MatchedPtr := InBin(Area_Info_Ptr, Area_Info_Size, UIntBuffer, 4))
57 | {
58 | if !Mod(Area_Info_Ptr - MatchedPtr, 12) ; if its the ID offset
59 | LMDMap[CRC32] := Name
60 | }
61 |
62 | NameSize++
63 | RemainingSize -= NameSize
64 | File_Ptr += NameSize
65 | }
66 |
67 | LMDFile_OffsetfromBody.Push(File_StartingOffsetfromBody)
68 | }
69 | FoundPtr++
70 | Seeker_RemainingBytes -= FoundPtr-Seeker
71 | Seeker := FoundPtr
72 | }
73 | Gui_ControlPanel_Progress.Value++
74 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
75 |
76 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Scan Info Area for possible field containing the LMD Information registration
77 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
78 |
79 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Scan Info Area for possible field containing the LMD Information registration
80 | LMDLookup := []
81 | InvalidInfo_Index := []
82 | Area_Info_ValidFieldsCount := 0
83 | Seeker := Area_Info_Ptr
84 | Loop Files_Count
85 | {
86 | File_ID := NumGet(Seeker+0, "UInt")
87 | Seeker += 0x4
88 | File_StartingOffsetfromBody := NumGet(Seeker+0, "UInt")
89 | Seeker += 0x4
90 | File_Size := NumGet(Seeker+0, "UInt")
91 | Seeker += 0x4
92 | File_EndingOffsetfromBody := File_StartingOffsetfromBody+File_Size
93 | if ((File_Size == 0) or (Area_Body_Size < File_EndingOffsetfromBody)) ; corrupted/invalid info
94 | {
95 | if (File_ID == 0x366E051F) ; was an LMD Info
96 | LMDLookup.Push({Index: A_Index-1, isHealthy: False})
97 | else InvalidInfo_Index.Push(A_Index-1)
98 | }
99 | else if (File_ID == 0x366E051F) ; was an LMD Info
100 | {
101 | LMDLookup.Push({Index: A_Index-1,
102 | isHealthy: InBin(Area_Body_Ptr + File_StartingOffsetfromBody, 32, LMD_Header_Pattern.Ptr, 32) ; check if this info field is pointing onto a valid LMD File
103 | })
104 | }
105 | else Area_Info_ValidFieldsCount++
106 | }
107 | Gui_ControlPanel_Progress.Value++
108 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~
109 |
110 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Stage 1: Create LookUpTable
111 | Seeker := MixFileObj.Ptr + Area_Info_Offset
112 | LookUpTable := Map()
113 | LookUpTable.Capacity := Files_Count
114 | Loop Files_Count
115 | {
116 | File_ID := NumGet(Seeker+0, "UInt")
117 | Seeker += 4
118 | File_StartingOffsetfromBody := NumGet(Seeker+0, "UInt")
119 | Seeker += 4
120 | File_Size := NumGet(Seeker+0, "UInt")
121 | Seeker += 4
122 | if (File_ID != 0x366E051F) ; Ignore LMD Files
123 | {
124 | File_EndingOffsetfromBody := File_StartingOffsetfromBody+File_Size
125 | if ((File_Size != 0) and (Area_Body_Size >= File_EndingOffsetfromBody)) ; valid info / not corrupted
126 | LookUpTable[A_Index-1] := {ID: File_ID, OffsetfromBody: File_StartingOffsetfromBody, Size: File_Size}
127 | }
128 | }
129 | Gui_ControlPanel_Progress.Value++
130 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
131 |
132 | /*
133 | Validate LookUpTable by Eliminating Garbage Informations:
134 | Case 1: one or more indexes have all their informations equal to each other. (ask user for help)
135 | Case 2: an index acting like a container was detected with one or more indexes inside it. (ask user for help)
136 | Case 3: an index overlaps with one or more indexes. (auto eliminate some garbage then ask user for help)
137 | */
138 | if (LookUpTable.Count >= 2)
139 | {
140 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Stage 2: Detect File Conflicts and count how much files did the suspected garbage file Overlapped to.
141 | DuplicateList := []
142 | ContainerMap := Map()
143 | OverlapMap := Map()
144 |
145 | ; we use our mcode from native c language to analyze conflicts because AHK execution time is so slow.
146 | LookUpTable_Count := LookUpTable.Count
147 | LookUpTable_Buffer := Buffer(LookUpTable_Count * 10)
148 | Seeker := LookUpTable_Buffer.Ptr
149 | For Key in LookUpTable
150 | {
151 | NumPut("UInt" , LookUpTable[Key].OffsetfromBody,
152 | "UInt" , LookUpTable[Key].OffsetfromBody+LookUpTable[Key].Size,
153 | "UShort", Key,
154 | Seeker)
155 | Seeker += 10
156 | }
157 | LookUpTable_API := Buffer(6 + A_PtrSize)
158 | NumPut("UShort", 0,
159 | "UShort", 0,
160 | "UShort", LookUpTable.Count,
161 | "Ptr" , LookUpTable_Buffer.Ptr,
162 | LookUpTable_API)
163 | ;
164 | while Result := DllCall(FileConflictChecker,"Ptr",LookUpTable_API.Ptr,"CDecl UChar")
165 | {
166 | Index1 := NumGet(LookUpTable_API, "UShort")
167 | Index2 := NumGet(LookUpTable_API, 2, "UShort")
168 | Key1 := NumGet(LookUpTable_Buffer, (Index1 * 10) + 8, "UShort")
169 | Key2 := NumGet(LookUpTable_Buffer, (Index2 * 10) + 8, "UShort")
170 | if LookUpTable.Has(Key1) and LookUpTable.Has(Key2) ; conflict confusion still exists
171 | {
172 | if LMDMap.Has(LookUpTable[Key1].ID) and !LMDMap.Has(LookUpTable[Key2].ID)
173 | LookUpTable.Delete(Key2) ; Key1 is registered in our LMD while Key2 is unregistered means that Key2 is automatically garbage
174 | else if !LMDMap.Has(LookUpTable[Key1].ID) and LMDMap.Has(LookUpTable[Key2].ID)
175 | LookUpTable.Delete(Key1) ; Key2 is registered in our LMD while Key1 is unregistered means that Key1 is automatically garbage
176 | else switch Result
177 | {
178 | case 1: ; Index1's information is a duplicate of Index2
179 | AppendNew := True
180 | Loop DuplicateList.Length
181 | {
182 | if DuplicateList[A_Index].Has(Key2)
183 | {
184 | AppendNew := False
185 | break
186 | }
187 | else if DuplicateList[A_Index].Has(Key1)
188 | {
189 | DuplicateList[A_Index][Key2] := True
190 | AppendNew := False
191 | break
192 | }
193 | }
194 | if AppendNew
195 | DuplicateList.Push(Map(Key1,True, Key2, True))
196 | case 2: ; Index1 contains Index2
197 | if ContainerMap.Has(Key1)
198 | {
199 | if !ContainerMap[Key1].Has(Key2)
200 | ContainerMap[Key1][Key2] := True
201 | }
202 | else ContainerMap[Key1] := Map(Key2, True)
203 | case 3: ; Index2 contains Index1
204 | if ContainerMap.Has(Key2)
205 | {
206 | if !ContainerMap[Key2].Has(Key1)
207 | ContainerMap[Key2][Key1] := True
208 | }
209 | else ContainerMap[Key2] := Map(Key1, True)
210 | case 4: ; Index1 overlaps Index2
211 | if OverlapMap.Has(Key1)
212 | OverlapMap[Key1][Key2] := True
213 | else OverlapMap[Key1] := Map(Key2, True)
214 | if OverlapMap.Has(Key2)
215 | OverlapMap[Key2][Key1] := True
216 | else OverlapMap[Key2] := Map(Key1, True)
217 | default:
218 | msgbox "INVALID CASE RESULT '" . Result . "' FOR CONFLICT DETECTION`n`nimmediately inform the developer of this tool"
219 | }
220 | }
221 | }
222 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
223 | }
224 | Gui_ControlPanel_Progress.Value++
--------------------------------------------------------------------------------
/Lib/Class_LV_Colors.ahk2:
--------------------------------------------------------------------------------
1 | ; ======================================================================================================================
2 | ; Namespace: LV_Colors
3 | ; Function: Individual row and cell coloring for AHK ListView controls.
4 | ; Tested with: AHK 2.0.2 (U32/U64)
5 | ; Tested on: Win 10 (x64)
6 | ; Changelog: 2023-01-04/2.0.0/just me Initial release of the AHK v2 version
7 | ; ======================================================================================================================
8 | ; CLASS LV_Colors
9 | ;
10 | ; The class provides methods to set individual colors for rows and/or cells, to clear all colors, to prevent/allow
11 | ; sorting and rezising of columns dynamically, and to deactivate/activate the notification handler for NM_CUSTOMDRAW
12 | ; notifications (see below).
13 | ;
14 | ; A message handler for NM_CUSTOMDRAW notifications will be activated for the specified ListView whenever a new
15 | ; instance is created. If you want to temporarily disable coloring call MyInstance.ShowColors(False). This must
16 | ; be done also before you try to destroy the instance. To enable it again, call MyInstance.ShowColors().
17 | ;
18 | ; To avoid the loss of Gui events and messages the message handler is set 'critical'. To prevent 'freezing' of the
19 | ; list-view or the whole GUI this script requires AHK v2.0.1+.
20 | ; ======================================================================================================================
21 | Class LV_Colors {
22 | ; ===================================================================================================================
23 | ; __New() Constructor - Create a new LV_Colors instance for the given ListView
24 | ; Parameters: HWND - ListView's HWND.
25 | ; Optional ------------------------------------------------------------------------------------------
26 | ; StaticMode - Static color assignment, i.e. the colors will be assigned permanently to the row
27 | ; contents rather than to the row number.
28 | ; Values: True/False
29 | ; Default: False
30 | ; NoSort - Prevent sorting by click on a header item.
31 | ; Values: True/False
32 | ; Default: False
33 | ; NoSizing - Prevent resizing of columns.
34 | ; Values: True/False
35 | ; Default: False
36 | ; ===================================================================================================================
37 | __New(LV, StaticMode := False, NoSort := False, NoSizing := False) {
38 | If (LV.Type != "ListView")
39 | Throw TypeError("LV_Colors requires a ListView control!", -1, LV.Type)
40 | ; ----------------------------------------------------------------------------------------------------------------
41 | ; Set LVS_EX_DOUBLEBUFFER (0x010000) style to avoid drawing issues.
42 | LV.Opt("+LV0x010000")
43 | ; Get the default colors
44 | BkClr := SendMessage(0x1025, 0, 0, LV) ; LVM_GETTEXTBKCOLOR
45 | TxClr := SendMessage(0x1023, 0, 0, LV) ; LVM_GETTEXTCOLOR
46 | ; Get the header control
47 | Header := SendMessage(0x101F, 0, 0, LV) ; LVM_GETHEADER
48 | ; Set other properties
49 | This.LV := LV
50 | This.HWND := LV.HWND
51 | This.Header := Header
52 | This.BkClr := BkCLr
53 | This.TxClr := Txclr
54 | This.IsStatic := !!StaticMode
55 | This.AltCols := False
56 | This.AltRows := False
57 | This.SelColors := False
58 | This.NoSort(!!NoSort)
59 | This.NoSizing(!!NoSizing)
60 | This.ShowColors()
61 | This.RowCount := LV.GetCount()
62 | This.ColCount := LV.GetCount("Col")
63 | This.Rows := Map()
64 | This.Rows.Capacity := This.RowCount
65 | This.Cells := Map()
66 | This.Cells.Capacity := This.RowCount
67 | }
68 | ; ===================================================================================================================
69 | ; __Delete() Destructor
70 | ; ===================================================================================================================
71 | __Delete() {
72 | This.ShowColors(False)
73 | If WinExist(This.HWND)
74 | WinRedraw(This.HWND)
75 | }
76 | ; ===================================================================================================================
77 | ; Clear() Clears all row and cell colors.
78 | ; Parameters: AltRows - Reset alternate row coloring (True / False)
79 | ; Default: False
80 | ; AltCols - Reset alternate column coloring (True / False)
81 | ; Default: False
82 | ; Return Value: Always True.
83 | ; ===================================================================================================================
84 | Clear(AltRows := False, AltCols := False) {
85 | If (AltCols)
86 | This.AltCols := False
87 | If (AltRows)
88 | This.AltRows := False
89 | This.Rows.Clear()
90 | This.Rows.Capacity := This.RowCount
91 | This.Cells.Clear()
92 | This.Cells.Capacity := This.RowCount
93 | Return True
94 | }
95 | ; ===================================================================================================================
96 | ; UpdateProps() Updates the RowCount, ColCount, BkClr, and TxClr properties.
97 | ; Return Value: True on success, otherwise false.
98 | ; ===================================================================================================================
99 | UpdateProps() {
100 | If !(This.HWND)
101 | Return False
102 | This.BkClr := SendMessage(0x1025, 0, 0, This.LV) ; LVM_GETTEXTBKCOLOR
103 | This.TxClr := SendMessage(0x1023, 0, 0, This.LV) ; LVM_GETTEXTCOLOR
104 | This.RowCount := This.LV.GetCount()
105 | This.Colcount := This.LV.GetCount("Col")
106 | If WinExist(This.HWND)
107 | WinRedraw(This.HWND)
108 | Return True
109 | }
110 | ; ===================================================================================================================
111 | ; AlternateRows() Sets background and/or text color for even row numbers.
112 | ; Parameters: BkColor - Background color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
113 | ; Default: Empty -> default background color
114 | ; TxColor - Text color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
115 | ; Default: Empty -> default text color
116 | ; Return Value: True on success, otherwise false.
117 | ; ===================================================================================================================
118 | AlternateRows(BkColor := "", TxColor := "") {
119 | If !(This.HWND)
120 | Return False
121 | This.AltRows := False
122 | If (BkColor = "") && (TxColor = "")
123 | Return True
124 | BkBGR := This.BGR(BkColor)
125 | TxBGR := This.BGR(TxColor)
126 | If (BkBGR = "") && (TxBGR = "")
127 | Return False
128 | This.ARB := (BkBGR != "") ? BkBGR : This.BkClr
129 | This.ART := (TxBGR != "") ? TxBGR : This.TxClr
130 | This.AltRows := True
131 | Return True
132 | }
133 | ; ===================================================================================================================
134 | ; AlternateCols() Sets background and/or text color for even column numbers.
135 | ; Parameters: BkColor - Background color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
136 | ; Default: Empty -> default background color
137 | ; TxColor - Text color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
138 | ; Default: Empty -> default text color
139 | ; Return Value: True on success, otherwise false.
140 | ; ===================================================================================================================
141 | AlternateCols(BkColor := "", TxColor := "") {
142 | If !(This.HWND)
143 | Return False
144 | This.AltCols := False
145 | If (BkColor = "") && (TxColor = "")
146 | Return True
147 | BkBGR := This.BGR(BkColor)
148 | TxBGR := This.BGR(TxColor)
149 | If (BkBGR = "") && (TxBGR = "")
150 | Return False
151 | This.ACB := (BkBGR != "") ? BkBGR : This.BkClr
152 | This.ACT := (TxBGR != "") ? TxBGR : This.TxClr
153 | This.AltCols := True
154 | Return True
155 | }
156 | ; ===================================================================================================================
157 | ; SelectionColors() Sets background and/or text color for selected rows.
158 | ; Parameters: BkColor - Background color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
159 | ; Default: Empty -> default selected background color
160 | ; TxColor - Text color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
161 | ; Default: Empty -> default selected text color
162 | ; Return Value: True on success, otherwise false.
163 | ; ===================================================================================================================
164 | SelectionColors(BkColor := "", TxColor := "") {
165 | If !(This.HWND)
166 | Return False
167 | This.SelColors := False
168 | If (BkColor = "") && (TxColor = "")
169 | Return True
170 | BkBGR := This.BGR(BkColor)
171 | TxBGR := This.BGR(TxColor)
172 | If (BkBGR = "") && (TxBGR = "")
173 | Return False
174 | This.SELB := BkBGR
175 | This.SELT := TxBGR
176 | This.SelColors := True
177 | Return True
178 | }
179 | ; ===================================================================================================================
180 | ; Row() Sets background and/or text color for the specified row.
181 | ; Parameters: Row - Row number
182 | ; Optional ------------------------------------------------------------------------------------------
183 | ; BkColor - Background color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
184 | ; Default: Empty -> default background color
185 | ; TxColor - Text color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
186 | ; Default: Empty -> default text color
187 | ; Return Value: True on success, otherwise false.
188 | ; ===================================================================================================================
189 | Row(Row, BkColor := "", TxColor := "") {
190 | If !(This.HWND)
191 | Return False
192 | If (Row >This.RowCount)
193 | Return False
194 | If This.IsStatic
195 | Row := This.MapIndexToID(Row)
196 | If This.Rows.Has(Row)
197 | This.Rows.Delete(Row)
198 | If (BkColor = "") && (TxColor = "")
199 | Return True
200 | BkBGR := This.BGR(BkColor)
201 | TxBGR := This.BGR(TxColor)
202 | If (BkBGR = "") && (TxBGR = "")
203 | Return False
204 | ; Colors := {B: (BkBGR != "") ? BkBGR : This.BkClr, T: (TxBGR != "") ? TxBGR : This.TxClr}
205 | This.Rows[Row] := Map("B", (BkBGR != "") ? BkBGR : This.BkClr, "T", (TxBGR != "") ? TxBGR : This.TxClr)
206 | Return True
207 | }
208 | ; ===================================================================================================================
209 | ; Cell() Sets background and/or text color for the specified cell.
210 | ; Parameters: Row - Row number
211 | ; Col - Column number
212 | ; Optional ------------------------------------------------------------------------------------------
213 | ; BkColor - Background color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
214 | ; Default: Empty -> row's background color
215 | ; TxColor - Text color as RGB color integer (e.g. 0xFF0000 = red) or HTML color name.
216 | ; Default: Empty -> row's text color
217 | ; Return Value: True on success, otherwise false.
218 | ; ===================================================================================================================
219 | Cell(Row, Col, BkColor := "", TxColor := "") {
220 | If !(This.HWND)
221 | Return False
222 | If (Row > This.RowCount) || (Col > This.ColCount)
223 | Return False
224 | If This.IsStatic
225 | Row := This.MapIndexToID(Row)
226 | If This.Cells.Has(Row) && This.Cells[Row].Has(Col)
227 | This.Cells[Row].Delete(Col)
228 | If (BkColor = "") && (TxColor = "")
229 | Return True
230 | BkBGR := This.BGR(BkColor)
231 | TxBGR := This.BGR(TxColor)
232 | If (BkBGR = "") && (TxBGR = "")
233 | Return False
234 | If !This.Cells.Has(Row)
235 | This.Cells[Row] := [], This.Cells[Row].Capacity := This.ColCount
236 | If (Col > This.Cells[Row].Length)
237 | This.Cells[Row].Length := Col
238 | This.Cells[Row][Col] := Map("B", (BkBGR != "") ? BkBGR : This.BkClr, "T", (TxBGR != "") ? TxBGR : This.TxClr)
239 | Return True
240 | }
241 | ; ===================================================================================================================
242 | ; NoSort() Prevents/allows sorting by click on a header item for this ListView.
243 | ; Parameters: Apply - True/False
244 | ; Return Value: True on success, otherwise false.
245 | ; ===================================================================================================================
246 | NoSort(Apply := True) {
247 | If !(This.HWND)
248 | Return False
249 | This.LV.Opt((Apply ? "+" : "-") . "NoSort")
250 | Return True
251 | }
252 | ; ===================================================================================================================
253 | ; NoSizing() Prevents/allows resizing of columns for this ListView.
254 | ; Parameters: Apply - True/False
255 | ; Return Value: True on success, otherwise false.
256 | ; ===================================================================================================================
257 | NoSizing(Apply := True) {
258 | If !(This.Header)
259 | Return False
260 | ControlSetStyle((Apply ? "+" : "-") . "0x0800", This.Header) ; HDS_NOSIZING = 0x0800
261 | Return True
262 | }
263 | ; ===================================================================================================================
264 | ; ShowColors() Adds/removes a message handler for NM_CUSTOMDRAW notifications of this ListView.
265 | ; Parameters: Apply - True/False
266 | ; Return Value: Always True
267 | ; ===================================================================================================================
268 | ShowColors(Apply := True) {
269 | If (Apply) && !This.HasOwnProp("OnNotifyFunc") {
270 | This.OnNotifyFunc := ObjBindMethod(This, "NM_CUSTOMDRAW")
271 | This.LV.OnNotify(-12, This.OnNotifyFunc)
272 | WinRedraw(This.HWND)
273 | }
274 | Else If !(Apply) && This.HasOwnProp("OnNotifyFunc") {
275 | This.LV.OnNotify(-12, This.OnNotifyFunc, 0)
276 | This.OnNotifyFunc := ""
277 | This.DeleteProp("OnNotifyFunc")
278 | WinRedraw(This.HWND)
279 | }
280 | Return True
281 | }
282 | ; ===================================================================================================================
283 | ; Internally used/called Methods
284 | ; ===================================================================================================================
285 | NM_CUSTOMDRAW(LV, L) {
286 | ; Return values: 0x00 (CDRF_DODEFAULT), 0x20 (CDRF_NOTIFYITEMDRAW / CDRF_NOTIFYSUBITEMDRAW)
287 | Static SizeNMHDR := A_PtrSize * 3 ; Size of NMHDR structure
288 | Static SizeNCD := SizeNMHDR + 16 + (A_PtrSize * 5) ; Size of NMCUSTOMDRAW structure
289 | Static OffItem := SizeNMHDR + 16 + (A_PtrSize * 2) ; Offset of dwItemSpec (NMCUSTOMDRAW)
290 | Static OffItemState := OffItem + A_PtrSize ; Offset of uItemState (NMCUSTOMDRAW)
291 | Static OffCT := SizeNCD ; Offset of clrText (NMLVCUSTOMDRAW)
292 | Static OffCB := OffCT + 4 ; Offset of clrTextBk (NMLVCUSTOMDRAW)
293 | Static OffSubItem := OffCB + 4 ; Offset of iSubItem (NMLVCUSTOMDRAW)
294 | Critical -1
295 | If !(This.HWND) || (NumGet(L, "UPtr") != This.HWND)
296 | Return
297 | ; ----------------------------------------------------------------------------------------------------------------
298 | DrawStage := NumGet(L + SizeNMHDR, "UInt"),
299 | Row := NumGet(L + OffItem, "UPtr") + 1,
300 | Col := NumGet(L + OffSubItem, "Int") + 1,
301 | Item := Row - 1
302 | If This.IsStatic
303 | Row := This.MapIndexToID(Row)
304 | ; CDDS_SUBITEMPREPAINT = 0x030001 --------------------------------------------------------------------------------
305 | If (DrawStage = 0x030001) {
306 | UseAltCol := (This.AltCols) && !(Col & 1),
307 | ColColors := (This.Cells.Has(Row) && This.Cells[Row].Has(Col)) ? This.Cells[Row][Col] : Map("B", "", "T", ""),
308 | ColB := (ColColors["B"] != "") ? ColColors["B"] : UseAltCol ? This.ACB : This.RowB,
309 | ColT := (ColColors["T"] != "") ? ColColors["T"] : UseAltCol ? This.ACT : This.RowT,
310 | NumPut("UInt", ColT, L + OffCT), NumPut("UInt", ColB, L + OffCB)
311 | Return (!This.AltCols && (Col > This.Cells[Row].Length)) ? 0x00 : 0x020
312 | }
313 | ; CDDS_ITEMPREPAINT = 0x010001 -----------------------------------------------------------------------------------
314 | If (DrawStage = 0x010001) {
315 | ; LVM_GETITEMSTATE = 0x102C, LVIS_SELECTED = 0x0002
316 | If (This.SelColors) && SendMessage(0x102C, Item, 0x0002, This.HWND) {
317 | ; Remove the CDIS_SELECTED (0x0001) and CDIS_FOCUS (0x0010) states from uItemState and set the colors.
318 | NumPut("UInt", NumGet(L + OffItemState, "UInt") & ~0x0011, L + OffItemState)
319 | If (This.SELB != "")
320 | NumPut("UInt", This.SELB, L + OffCB)
321 | If (This.SELT != "")
322 | NumPut("UInt", This.SELT, L + OffCT)
323 | Return 0x02 ; CDRF_NEWFONT
324 | }
325 | UseAltRow := This.AltRows && (Item & 1),
326 | RowColors := This.Rows.Has(Row) ? This.Rows[Row] : "",
327 | This.RowB := RowColors ? RowColors["B"] : UseAltRow ? This.ARB : This.BkClr,
328 | This.RowT := RowColors ? RowColors["T"] : UseAltRow ? This.ART : This.TxClr
329 | If (This.AltCols || This.Cells.Has(Row))
330 | Return 0x20
331 | NumPut("UInt", This.RowT, L + OffCT), NumPut("UInt", This.RowB, L + OffCB)
332 | Return 0x00
333 | }
334 | ; CDDS_PREPAINT = 0x000001 ---------------------------------------------------------------------------------------
335 | Return (DrawStage = 0x000001) ? 0x20 : 0x00
336 | }
337 | ; -------------------------------------------------------------------------------------------------------------------
338 | MapIndexToID(Row) { ; provides the unique internal ID of the given row number
339 | Return SendMessage(0x10B4, Row - 1, 0, This.HWND) ; LVM_MAPINDEXTOID
340 | }
341 | ; -------------------------------------------------------------------------------------------------------------------
342 | BGR(Color, Default := "") { ; converts colors to BGR
343 | ; HTML Colors (BGR)
344 | Static HTML := {AQUA: 0xFFFF00, BLACK: 0x000000, BLUE: 0xFF0000, FUCHSIA: 0xFF00FF, GRAY: 0x808080, GREEN: 0x008000
345 | , LIME: 0x00FF00, MAROON: 0x000080, NAVY: 0x800000, OLIVE: 0x008080, PURPLE: 0x800080, RED: 0x0000FF
346 | , SILVER: 0xC0C0C0, TEAL: 0x808000, WHITE: 0xFFFFFF, YELLOW: 0x00FFFF}
347 | If IsInteger(Color)
348 | Return ((Color >> 16) & 0xFF) | (Color & 0x00FF00) | ((Color & 0xFF) << 16)
349 | Return (HTML.HasOwnProp(Color) ? HTML.%Color% : Default)
350 | }
351 | }
--------------------------------------------------------------------------------
/reMIXer.ahk2:
--------------------------------------------------------------------------------
1 | #SingleInstance Force
2 | #Warn
3 | KeyHistory 0
4 | ListLines False
5 | SetWorkingDir A_ScriptDir
6 |
7 | ; #include "Lib\MCodes.ahk2"
8 | #DllLoad "%A_ScriptDir%\reMIXer.dll"
9 | DLLHandle := DllCall("GetModuleHandle", "Str", "reMIXer", "Ptr")
10 | if !DLLHandle
11 | {
12 | MsgBox "Failed to load reMIXer.dll!"
13 | ExitApp
14 | }
15 | FileConflictChecker := DllCall("GetProcAddress", "Ptr", DLLHandle, "AStr", "FileConflictChecker", "Ptr")
16 | ReconstructMIX := DllCall("GetProcAddress", "Ptr", DLLHandle, "AStr", "ReconstructMIX", "Ptr")
17 |
18 | #include "Lib\Class_LV_Colors.ahk2"
19 |
20 | AppVersion := "3.1.0.0"
21 |
22 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Construct the Control Panel GUI
23 | Gui_ControlPanel := Gui("OwnDialogs ", "RA2YR reMIXer v" . AppVersion . (A_IsAdmin?" (Administrator)":""))
24 | Gui_ControlPanel.BackColor := "72787E"
25 | Gui_ControlPanel.SetFont("cWhite", "Verdana")
26 | Gui_ControlPanel.AddButton("vAddButton Y10 H20", "Add Target Archives to List")
27 | Gui_ControlPanel["AddButton"].OnEvent("Click", AddMIXtoList)
28 | Gui_ControlPanel.AddButton("vreMIXAllTargets X+m H20", "reMIX All Targets")
29 | Gui_ControlPanel["reMIXAllTargets"].OnEvent("Click", reMIXAllMIXTargets)
30 | Gui_ControlPanel.AddButton("vHowToUse X+m H20", "How to Use RA2YR reMIXer?")
31 | Gui_ControlPanel["HowToUse"].OnEvent("Click", Gui_ControlPanel_HowToUseMsg)
32 | Gui_ControlPanel.AddText("XM Y+10", "Mode:")
33 | Gui_ControlPanel.AddDDL("vModeSelector R3 Choose3 X+2 H20 W" . PercentageToScreenWidth(39.2), ["1: Only Fix Header","2(Recommended): Recover LMD Lookup Table -> Fix Header", "3(Advanced): Reconstruct MIX Archive -> Recover LMD Lookup Table -> Fix Header"])
34 | Gui_ControlPanel.AddListView("vLV Grid Background303841 Checked -LV0x10 -WantF2 Xs Y+10 W" . PercentageToScreenWidth(41) . " H" . PercentageToScreenHeight(45), ["Header Status", "LMD Status", "File Path",""])
35 | Gui_ControlPanel_LV_Menu := Menu()
36 | Gui_ControlPanel_LV_Menu.Add("Delete All Selected Rows", Gui_ControlPanel_LV_DeleteRows)
37 | Gui_ControlPanel_LV_Colors := LV_Colors(Gui_ControlPanel["LV"]) ; Create a new instance of LV_Colors
38 | If !IsObject(Gui_ControlPanel_LV_Colors)
39 | MsgBox("Couldn't create a new LV_Colors object!", "ERROR", 16)
40 | Gui_ControlPanel_LV_Colors.ShowColors(True)
41 | Gui_ControlPanel["LV"].OnEvent("ContextMenu", Gui_ControlPanel_LV_Menu_Show)
42 | Gui_ControlPanel["LV"].OnEvent("ColClick", Gui_ControlPanel_LV_UpdateColors)
43 | Gui_ControlPanel_Progress := Gui_ControlPanel.AddProgress("-Smooth Range Xs Y+10 W" . PercentageToScreenWidth(41) . " H" . PercentageToScreenHeight(2), 100)
44 | Gui_ControlPanel_Progress.Visible := False
45 | Gui_ControlPanel_Progress.Value := 0
46 | Gui_ControlPanel.OnEvent("DropFiles", DragAndDropMIXesToList)
47 | Gui_ControlPanel.Show("Center")
48 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
49 |
50 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Construct the LMD Selector GUI
51 | Gui_LMDSelector := Gui("AlwaysOnTop OwnDialogs Owner" . Gui_ControlPanel.Hwnd, "LMD Selector")
52 | Gui_LMDSelector.BackColor := "72787E"
53 | Gui_LMDSelector.SetFont("cWhite", "Verdana")
54 | Gui_LMDSelector.AddText("W" . 0.25*A_ScreenWidth,"Multiple LMD files have been detected inside the target MIX Archive:")
55 | Gui_LMDSelector_FilePath := Gui_LMDSelector.AddEdit("ReadOnly Background303841 W" . 0.25*A_ScreenWidth)
56 | Gui_LMDSelector.AddText("W" . 0.25*A_ScreenWidth,"
57 | (
58 |
59 | Please select only one among the list.
60 |
61 | Legend:
62 | Offset From Body - distance in address from the body address, where our LMD File can be found.
63 | Size - LMD File Size in bytes.
64 | Names Count - how many entries are listed in the dictionary of this LMD File. These entries are used for defining all possible file names that this MIX Archive contain.
65 | Status:
66 | * Default - this LMD file is what our MIX Archive is currently using.
67 | * Unused - this LMD exists inside our MIX Archive but wasn't used by default.
68 |
69 | - Keep in mind that selecting a wrong LMD file will result some files to have incorrect names when viewing the mix. If this happens, redo the reMIXing and select another LMD file from the list, until majority of the file names are now viewable at the XCC Mixer Application.
70 | - Close this GUI to finalize your selection
71 | - Closing this GUI without selecting anything from the list will choose the "Default" LMD File. Incase there is no "Default" LMD File, the LMD File with the highest Offset from the Body will be chosen.
72 | )")
73 | Gui_LMDSelector_LV := Gui_LMDSelector.AddListView("Grid Background303841 -LV0x10 -Multi -WantF2 W" . 0.25*A_ScreenWidth . " H" . 0.25*A_ScreenHeight, ["Relative Offset From Body", "Size", "Names Count", "Status",""])
74 | Gui_LMDSelector.OnEvent("Close", LMDSelector_Select)
75 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
76 |
77 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Construct the Conflict Solver GUI
78 | Gui_ConflictSolver := Gui("AlwaysOnTop OwnDialogs Owner" . Gui_ControlPanel.Hwnd, "Conflict Solver")
79 | Gui_ConflictSolver.BackColor := "72787E"
80 | Gui_ConflictSolver.SetFont("cWhite", "Verdana")
81 | Gui_ConflictSolver_Comment := Gui_ConflictSolver.AddEdit("ReadOnly Background303841 W" . PercentageToScreenWidth(50.5) . " H" . PercentageToScreenHeight(20))
82 | Gui_ConflictSolver.AddButton("vCancelMIXReconstruction XM H20 ", "Cancel MIX Reconstruction")
83 | Gui_ConflictSolver["CancelMIXReconstruction"].OnEvent("Click", Gui_ConflictSolver_CancelMIXReconstruction_OnClick)
84 | Gui_ConflictSolver.AddText("vBytesConflict Center Background303841 X+m W" . PercentageToScreenWidth(32.5),"37213217 Bytes Saved")
85 | Gui_ConflictSolver_DuplicateLV := Gui_ConflictSolver.AddListView("Grid Background303841 -LV0x10 -Multi -WantF2 XM W" . PercentageToScreenWidth(25) . " H" . PercentageToScreenHeight(30), ["Chosen Index(ID)", "Duplicate Group of Indexes(ID)", "Relative Offset From Body", "Size",""])
86 | Gui_ConflictSolver_DuplicateLV_Colors := LV_Colors(Gui_ConflictSolver_DuplicateLV) ; Create a new instance of LV_Colors
87 | If !IsObject(Gui_ConflictSolver_DuplicateLV_Colors)
88 | MsgBox("Couldn't create a new LV_Colors object!", "ERROR", 16)
89 | Gui_ConflictSolver_DuplicateLV_Colors.ShowColors(True)
90 | Gui_ConflictSolver_DuplicateLV_Menu := Menu()
91 | Gui_ConflictSolver_DuplicateLV.OnEvent("DoubleClick", Gui_ConflictSolver_DuplicateLV_Menu_Show)
92 | Gui_ConflictSolver_DuplicateLV.OnEvent("ColClick", Gui_ConflictSolver_DuplicateLV_UpdateColors)
93 |
94 | Gui_ConflictSolver_ContainerLV := Gui_ConflictSolver.AddListView("Grid Background303841 Checked -LV0x10 -Multi -WantF2 X+m YP W" . PercentageToScreenWidth(25) . " H" . PercentageToScreenHeight(30), ["Container Index(ID)", "Contained Group of Indexes(ID)", "Relative Offset From Body", "Size",""])
95 | Gui_ConflictSolver_ContainerLV_Colors := LV_Colors(Gui_ConflictSolver_ContainerLV) ; Create a new instance of LV_Colors
96 | If !IsObject(Gui_ConflictSolver_ContainerLV_Colors)
97 | MsgBox("Couldn't create a new LV_Colors object!", "ERROR", 16)
98 | Gui_ConflictSolver_ContainerLV_Colors.ShowColors(True)
99 | Gui_ConflictSolver_ContainerLV.OnEvent("ItemCheck", Gui_ConflictSolver_ContainerLV_ItemCheck)
100 | Gui_ConflictSolver_ContainerLV.OnEvent("ColClick", Gui_ConflictSolver_ContainerLV_UpdateColors)
101 |
102 | Gui_ConflictSolver_DuplicateLV.GetPos(&ctrlposX, &ctrlposY)
103 | Gui_ConflictSolver_OverlapLV := Gui_ConflictSolver.AddListView("Grid Background303841 Checked -LV0x10 -Multi -WantF2 X" . ctrlposX . " Y" . ctrlposY . " W" . PercentageToScreenWidth(50.5) . " H" . PercentageToScreenHeight(30), ["Index", "Overlapped Group of Indexes", "Relative Offset From Body", "Size",""])
104 | Gui_ConflictSolver_OverlapLV_Colors := LV_Colors(Gui_ConflictSolver_OverlapLV) ; Create a new instance of LV_Colors
105 | If !IsObject(Gui_ConflictSolver_OverlapLV_Colors)
106 | MsgBox("Couldn't create a new LV_Colors object!", "ERROR", 16)
107 | Gui_ConflictSolver_OverlapLV_Colors.ShowColors(True)
108 | Gui_ConflictSolver_OverlapLV.OnEvent("ItemCheck", Gui_ConflictSolver_OverlapLV_ItemCheck)
109 | Gui_ConflictSolver_OverlapLV.OnEvent("ColClick", Gui_ConflictSolver_OverlapLV_UpdateColors)
110 | Gui_ConflictSolver.OnEvent("Close", ConflicSolver_Submit)
111 | ConflictSolver_ChangeMode(0)
112 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
113 | return
114 |
115 | Gui_ControlPanel_HowToUseMsg(GuiObj, Info)
116 | {
117 | MsgBox("
118 | (
119 | 1) Click Add Target Archives to List then select the MIX Archives you wish to analyze.
120 | 1.1) You can optionally delete a multiple rows on the MIX ListView by Right-Clicking them, then press Delete.
121 | 2) Put a Check Mark at the MIX Archives you wish to reMIX.
122 | 3) Change the reMIX "Mode" depending on your needs:
123 | * 1: File Size will not be reduced. The File Names will not be recovered. Only The Header will be recovered.
124 | * 2(Recommended): File Size will not be reduced. The Header will be recovered, and will try to recover some File Names inside the Target MIX Archive.
125 | * 3(Advanced): File Size will be reduced. The Header will be recovered, and will try to recover some File Names inside the Target MIX Archive.
126 | 4) Press "reMIX all Targets" button.
127 | 4.1) Keep in mind that there is NO guarantee that ALL file names will be recovered. Some file names could still not show properly.
128 | 5) Wait for the reMIXing to Finish.
129 | 6) Each Successfully reMIXed File can be found at the same directory as its original file. The name will start with word reMIXed_
130 |
131 |
132 | For more Information about this Tool, Visit:
133 | https://github.com/Aldrin-John-Olaer-Manalansan/RA2YR-reMIXer
134 |
135 | If you have problems/errors/concerns/suggestions when using this tool, create an issue at:
136 | https://github.com/Aldrin-John-Olaer-Manalansan/RA2YR-reMIXer/issues
137 | )", "How to Use RA2YR reMIXer?", "Owner" . Gui_ControlPanel.Hwnd)
138 | }
139 |
140 | DragAndDropMIXesToList(GuiObj, GuiCtrlObj, SelectedFiles, X, Y)
141 | {
142 | if (GuiCtrlObj == Gui_ControlPanel["LV"])
143 | AddMIXesToTargetList(SelectedFiles)
144 | }
145 |
146 | ComputeOutputFileSize(LookUpTable)
147 | {
148 | OutputFileSize := (LookUpTable.Count * 12) + 10
149 | For Key in LookUpTable
150 | OutputFileSize += LookUpTable[Key].Size
151 | return OutputFileSize
152 | }
153 |
154 | ChangeBytesConflictText(OutputSize, MaxSize)
155 | {
156 | RemainingSize := MaxSize - OutputSize
157 | Gui_ConflictSolver["BytesConflict"].Value := "MIX Size Before/After Reconstruction: " . OutputSize . " / " . MaxSize . " ("
158 | if (RemainingSize < 0)
159 | {
160 | Gui_ConflictSolver["BytesConflict"].SetFont("cRed bold")
161 | Gui_ConflictSolver["BytesConflict"].Value .= -RemainingSize . " Bytes Overflowed"
162 | }
163 | else
164 | {
165 | Gui_ConflictSolver["BytesConflict"].SetFont("c00FF7F bold")
166 | if (RemainingSize > 0)
167 | Gui_ConflictSolver["BytesConflict"].Value .= RemainingSize . " Bytes Reduced"
168 | else Gui_ConflictSolver["BytesConflict"].Value := "File Size Unchanged"
169 | }
170 | Gui_ConflictSolver["BytesConflict"].Value .= ")"
171 | }
172 |
173 | ConflictSolver_ChangeMode(Mode)
174 | {
175 | if Mode
176 | {
177 | Gui_ConflictSolver_DuplicateLV.Enabled := False
178 | Gui_ConflictSolver_DuplicateLV.Visible := False
179 | Gui_ConflictSolver_ContainerLV.Enabled := False
180 | Gui_ConflictSolver_ContainerLV.Visible := False
181 | Gui_ConflictSolver_OverlapLV.Enabled := True
182 | Gui_ConflictSolver_OverlapLV.Visible := True
183 | Gui_ConflictSolver["BytesConflict"].Visible := True
184 | }
185 | else
186 | {
187 | Gui_ConflictSolver_DuplicateLV.Enabled := True
188 | Gui_ConflictSolver_DuplicateLV.Visible := True
189 | Gui_ConflictSolver_ContainerLV.Enabled := True
190 | Gui_ConflictSolver_ContainerLV.Visible := True
191 | Gui_ConflictSolver_OverlapLV.Enabled := False
192 | Gui_ConflictSolver_OverlapLV.Visible := False
193 | Gui_ConflictSolver["BytesConflict"].Visible := False
194 | }
195 | }
196 |
197 | Gui_ConflictSolver_CancelMIXReconstruction_OnClick(GuiObj := "", Info := "")
198 | {
199 | Gui_ConflictSolver.Hide()
200 | global Gui_ConflictSolver_Status := -1
201 | }
202 |
203 | Gui_ControlPanel_LV_UpdateColors(*)
204 | {
205 | Gui_ControlPanel["LV"].Opt("-Redraw")
206 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
207 | Loop Gui_ControlPanel["LV"].GetCount()
208 | {
209 | GetColorFromStatus(Gui_ControlPanel["LV"].GetText(A_Index,1),&Color_BG,&Color_TX)
210 | Gui_ControlPanel_LV_Colors.Cell(A_Index, 1, Color_BG, Color_TX)
211 | GetColorFromStatus(Gui_ControlPanel["LV"].GetText(A_Index,2),&Color_BG,&Color_TX)
212 | Gui_ControlPanel_LV_Colors.Cell(A_Index, 2, Color_BG, Color_TX)
213 | }
214 | Gui_ControlPanel["LV"].Opt("+Redraw")
215 | }
216 |
217 | Gui_ConflictSolver_OverlapLV_ItemCheck(GuiCtrlObj, Row, Checked)
218 | {
219 | if Checked and (Gui_ConflictSolver_OverlapLV.GetText(Row,2) == "")
220 | { ; if this row was already settled
221 | Gui_ConflictSolver_OverlapLV.Modify(Row, "-Check")
222 | return
223 | }
224 | Gui_ConflictSolver_OverlapLV.Opt("-Redraw")
225 | ConflictMap := PublicAPIValue()
226 | GarbageMap := Map()
227 | OutputFileSize := ComputeOutputFileSize(ConflictMap.LookUpTable)
228 | Loop Gui_ConflictSolver_OverlapLV.GetCount()
229 | {
230 | if (Gui_ConflictSolver_OverlapLV.GetNext(A_Index-1,"C") == A_Index)
231 | {
232 | Key1 := Gui_ConflictSolver_OverlapLV.GetText(A_Index,1)
233 | Key1 := SubStr(Key1, 1, InStr(Key1, "(")-1)+0
234 | GarbageMap[Key1] := True
235 | OutputFileSize -= ConflictMap.LookUpTable[Key1].Size
236 | }
237 | }
238 | ChangeBytesConflictText(OutputFileSize, ConflictMap.MaxSize)
239 | Gui_ConflictSolver_OverlapLV_Colors.UpdateProps() ; sync LV info to the CLV
240 | Loop Gui_ConflictSolver_OverlapLV.GetCount()
241 | {
242 | if (Gui_ConflictSolver_OverlapLV.GetNext(A_Index-1,"C") == A_Index)
243 | {
244 | Gui_ConflictSolver_OverlapLV.Modify(A_Index, "Col2", "")
245 | Gui_ConflictSolver_OverlapLV_Colors.Row(A_Index, 0x00FF7F, 0x000000)
246 | }
247 | else
248 | {
249 | Key1 := Gui_ConflictSolver_OverlapLV.GetText(A_Index,1)
250 | Key1 := SubStr(Key1, 1, InStr(Key1, "(")-1)+0
251 | IndexesGroup := ""
252 | For Key2 in ConflictMap.OverlapMap[Key1]
253 | {
254 | if !GarbageMap.Has(Key2)
255 | {
256 | if (IndexesGroup != "")
257 | IndexesGroup .= " , "
258 | IndexesGroup .= Key2 . "(" . Format("{:08X}",ConflictMap.LookUpTable[Key2].ID) . ")"
259 | }
260 | }
261 | Gui_ConflictSolver_OverlapLV.Modify(A_Index, "Col2", IndexesGroup)
262 | if (IndexesGroup == "")
263 | Gui_ConflictSolver_OverlapLV_Colors.Row(A_Index, 0x00FF7F, 0x000000)
264 | else Gui_ConflictSolver_OverlapLV_Colors.Row(A_Index, 0xFF0000, 0xFFFFFF)
265 | }
266 | }
267 | Loop Gui_ConflictSolver_OverlapLV.GetCount("Column") - 1
268 | Gui_ConflictSolver_OverlapLV.ModifyCol(A_Index, "AutoHdr Center")
269 | Gui_ConflictSolver_OverlapLV.Opt("+Redraw")
270 | }
271 |
272 | Gui_ConflictSolver_OverlapLV_UpdateColors(*)
273 | {
274 | Gui_ConflictSolver_OverlapLV_Colors.UpdateProps() ; sync LV info to the CLV
275 | Loop Gui_ConflictSolver_OverlapLV.GetCount()
276 | {
277 | if (Gui_ConflictSolver_OverlapLV.GetText(A_Index,2) == "")
278 | Gui_ConflictSolver_OverlapLV_Colors.Row(A_Index, 0x00FF7F, 0x000000)
279 | else Gui_ConflictSolver_OverlapLV_Colors.Row(A_Index, 0xFF0000, 0xFFFFFF)
280 | }
281 | }
282 |
283 | Gui_ConflictSolver_ContainerLV_ItemCheck(GuiCtrlObj, Row, Checked)
284 | {
285 | Gui_ConflictSolver_ContainerLV_Colors.UpdateProps() ; sync LV info to the CLV
286 | Gui_ConflictSolver_ContainerLV_Colors.Row(Row, Checked?0xFF8B00:0xFFD700, 0x000000)
287 | }
288 |
289 | Gui_ConflictSolver_ContainerLV_UpdateColors(*)
290 | {
291 | Gui_ConflictSolver_ContainerLV.Opt("-Redraw")
292 | Gui_ConflictSolver_ContainerLV_Colors.UpdateProps() ; sync LV info to the CLV
293 | Loop Gui_ConflictSolver_ContainerLV.GetCount()
294 | Gui_ConflictSolver_ContainerLV_Colors.Row(A_Index, (Gui_ConflictSolver_ContainerLV.GetNext(A_Index-1,"C") == A_Index)?0xFF8B00:0xFFD700, 0x000000)
295 | Gui_ConflictSolver_ContainerLV.Opt("+Redraw")
296 | }
297 |
298 | Gui_ConflictSolver_DuplicateLV_UpdateColors(*)
299 | {
300 | Gui_ConflictSolver_DuplicateLV.Opt("-Redraw")
301 | Gui_ConflictSolver_DuplicateLV_Colors.UpdateProps() ; sync LV info to the CLV
302 | Loop Gui_ConflictSolver_DuplicateLV.GetCount()
303 | {
304 | if IsInteger(Gui_ConflictSolver_DuplicateLV.GetText(A_Index,1))
305 | Gui_ConflictSolver_DuplicateLV_Colors.Row(A_Index, 0x00FF7F, 0x000000)
306 | else Gui_ConflictSolver_DuplicateLV_Colors.Row(A_Index, 0xFF0000, 0xFFFFFF)
307 | }
308 | Gui_ConflictSolver_DuplicateLV.Opt("+Redraw")
309 | }
310 |
311 | Gui_ConflictSolver_DuplicateLV_Menu_Show(LV, Row)
312 | {
313 | if Row
314 | {
315 | Gui_ConflictSolver_DuplicateLV_Menu.Delete()
316 | Loop Parse Gui_ConflictSolver_DuplicateLV.GetText(Row,2), ",", A_Space
317 | Gui_ConflictSolver_DuplicateLV_Menu.Add(A_LoopField, Gui_ConflictSolver_DuplicateLV_SelectIndex, "BarBreak")
318 | MouseGetPos &X, &Y
319 | Gui_ConflictSolver_DuplicateLV_Menu.Show(X, Y)
320 | }
321 | }
322 |
323 | Gui_ConflictSolver_DuplicateLV_SelectIndex(ItemName, ItemPos, MyMenu)
324 | {
325 | Row := Gui_ConflictSolver_DuplicateLV.GetNext()
326 | Gui_ConflictSolver_DuplicateLV.Modify(Row, "-Select Col1", ItemName)
327 | Gui_ConflictSolver_DuplicateLV.ModifyCol(1, "AutoHdr Integer Center")
328 | Gui_ConflictSolver_DuplicateLV_Colors.UpdateProps() ; sync LV info to the CLV
329 | Gui_ConflictSolver_DuplicateLV_Colors.Row(Row, 0x00FF7F, 0x000000)
330 | }
331 |
332 | AddMIXesToTargetList(SelectedFiles)
333 | {
334 | if (SelectedFiles.Length > 0)
335 | {
336 | Gui_ControlPanel.Opt("+Disabled")
337 | Gui_ControlPanel_Progress.Value := 0
338 | Gui_ControlPanel_Progress.Opt("Range0-" . SelectedFiles.Length * 4)
339 | Gui_ControlPanel_Progress.Visible := True
340 |
341 | DuplicateChecker := ""
342 | RowCount := Gui_ControlPanel["LV"].GetCount()
343 | Loop RowCount
344 | DuplicateChecker .= "<|>" . Gui_ControlPanel["LV"].GetText(A_Index,3)
345 | Loop SelectedFiles.Length
346 | {
347 | if !InStr(DuplicateChecker, "<|>" . SelectedFiles[A_Index])
348 | {
349 | Gui_ControlPanel["LV"].Add(, , , SelectedFiles[A_Index])
350 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
351 | Gui_ControlPanel["LV"].ModifyCol(3, "AutoHdr Text Left")
352 | RowCount++
353 | MixFileObj := FileRead(SelectedFiles[A_Index], "RAW")
354 |
355 | MixHeaderStatus := GetMixHeaderStatus(&MixFileObj)
356 | Gui_ControlPanel["LV"].Modify(RowCount, "Col1", MixHeaderStatus)
357 | Gui_ControlPanel["LV"].ModifyCol(1, "AutoHdr Text Center")
358 | GetColorFromStatus(Gui_ControlPanel["LV"].GetText(RowCount,1),&Color_BG,&Color_TX)
359 | Gui_ControlPanel_LV_Colors.Cell(RowCount, 1, Color_BG, Color_TX)
360 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
361 |
362 | LMDStatus := GetLMDStatus(&MixFileObj)
363 | Gui_ControlPanel["LV"].Modify(
364 | RowCount,
365 | (((MixHeaderStatus == LMDStatus) and (MixHeaderStatus == "Healthy")) or InStr(MixHeaderStatus, "Unknown"))
366 | ? "Col2"
367 | : "Check Col2"
368 | , LMDStatus)
369 | Gui_ControlPanel["LV"].ModifyCol(2, "AutoHdr Text Center")
370 | GetColorFromStatus(Gui_ControlPanel["LV"].GetText(RowCount,2),&Color_BG,&Color_TX)
371 | Gui_ControlPanel_LV_Colors.Cell(RowCount, 2, Color_BG, Color_TX)
372 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
373 | }
374 | }
375 |
376 | Gui_ControlPanel_Progress.Visible := False
377 | Gui_ControlPanel.Opt("-Disabled")
378 | Gui_ControlPanel.Show()
379 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
380 | }
381 | }
382 |
383 | AddMIXtoList(GuiObj := "", Info := "")
384 | {
385 | SelectedFiles := FileSelect("M3", , "Select Target Archives")
386 | AddMIXesToTargetList(SelectedFiles)
387 | }
388 |
389 | Gui_ControlPanel_LV_Menu_Show(LV, Item, IsRightClick, X, Y)
390 | {
391 | Gui_ControlPanel_LV_Menu.Show(X, Y)
392 | }
393 |
394 | Gui_ControlPanel_LV_DeleteRows(*)
395 | {
396 | Count := Gui_ControlPanel["LV"].GetCount()
397 | if (Count > 0)
398 | {
399 | Gui_ControlPanel["LV"].Opt("-Redraw")
400 | Loop Count
401 | {
402 | Index := Count-A_Index+1
403 | if (Gui_ControlPanel["LV"].GetNext(Index-1) == Index)
404 | Gui_ControlPanel["LV"].Delete(Index)
405 | }
406 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
407 | Gui_ControlPanel["LV"].Opt("+Redraw")
408 | }
409 | }
410 |
411 | reMIXAllMIXTargets(GuiObj, Info)
412 | {
413 | Gui_ControlPanel.Opt("+Disabled")
414 | Count := Gui_ControlPanel["LV"].GetCount()
415 | Gui_ControlPanel_Progress.Visible := True
416 | Gui_ControlPanel_Progress.Value := 0
417 | reMIX_Mode := Gui_ControlPanel["ModeSelector"].Value
418 | CheckedCount := 0
419 | Loop Count
420 | {
421 | if (Gui_ControlPanel["LV"].GetNext(A_Index-1,"C") == A_Index)
422 | CheckedCount++
423 | }
424 | if (reMIX_Mode == 1)
425 | Gui_ControlPanel_Progress.Opt("Range0-" . CheckedCount * 2)
426 | else if (reMIX_Mode == 2)
427 | Gui_ControlPanel_Progress.Opt("Range0-" . CheckedCount * 8)
428 | else if (reMIX_Mode == 3)
429 | Gui_ControlPanel_Progress.Opt("Range0-" . CheckedCount * 11)
430 | Loop Count
431 | {
432 | Index := Count-A_Index+1
433 | if (Gui_ControlPanel["LV"].GetNext(Index-1,"C") == Index)
434 | {
435 | Status := reMIX_Level_%reMIX_Mode%(Gui_ControlPanel["LV"].GetText(Index,3))
436 | if (Status == -1) ; the user tells us to cancel reconstruction but instead just build the MIX File
437 | Status := reMIX_Level_2(Gui_ControlPanel["LV"].GetText(Index,3))
438 | if Status
439 | {
440 | Gui_ControlPanel["LV"].Delete(Index)
441 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
442 | }
443 | }
444 | }
445 | Gui_ControlPanel_Progress.Visible := False
446 | Gui_ControlPanel.Opt("-Disabled")
447 | Gui_ControlPanel.Show()
448 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
449 | }
450 |
451 | ConflicSolver_Submit(GuiObj)
452 | {
453 | isSettled := True
454 | if Gui_ConflictSolver_OverlapLV.Visible
455 | {
456 | Loop Gui_ConflictSolver_OverlapLV.GetCount()
457 | {
458 | if (Gui_ConflictSolver_OverlapLV.GetText(A_Index,2) != "")
459 | {
460 | isSettled := False
461 | SetTimer ReOpenConflictSolver, -250
462 | break
463 | }
464 | }
465 | }
466 | else
467 | {
468 | Loop Gui_ConflictSolver_DuplicateLV.GetCount()
469 | {
470 | if (Gui_ConflictSolver_DuplicateLV.GetText(A_Index,1) == "")
471 | {
472 | isSettled := False
473 | SetTimer ReOpenConflictSolver, -250
474 | break
475 | }
476 | }
477 | }
478 | if isSettled
479 | global Gui_ConflictSolver_Status := False
480 | }
481 |
482 | ReOpenConflictSolver()
483 | {
484 | Gui_ConflictSolver.Show("Center")
485 | Msgbox "There are still rows that you still haven't settled yet(Marked with Red Color). Choose a specific index among the duplicate group by Double-Clicking a Row with Red Color.", "Unsettled Duplicates Detected!", "Owner" . Gui_ConflictSolver.Hwnd
486 | }
487 |
488 | LMDSelector_Select(GuiObj)
489 | {
490 | SelectedRow := Gui_LMDSelector_LV.GetNext()
491 | if !SelectedRow
492 | SelectedRow := LMDSelector_AutoPick()
493 | Address := Gui_LMDSelector_LV.GetText(SelectedRow)+0
494 | if (Address != "")
495 | {
496 | Gui_LMDSelector.Hide()
497 | PublicAPIValue(Address)
498 | }
499 | }
500 |
501 | PublicAPIValue(NewValue := "")
502 | {
503 | static Value := 0
504 | if (NewValue != "")
505 | Value := NewValue
506 | return Value
507 | }
508 |
509 | LMDSelector_AutoPick()
510 | {
511 | SelectedRow := 0
512 | Highest_RowIndex := 1
513 | Highest_Offset := 0
514 | Loop Gui_LMDSelector_LV.GetCount()
515 | {
516 | if (Gui_LMDSelector_LV.GetText(A_Index,4) == "Default")
517 | {
518 | SelectedRow := A_Index
519 | break
520 | }
521 | Count := Gui_LMDSelector_LV.GetText(A_Index,1)
522 | if (Highest_Offset < Count)
523 | {
524 | Highest_Offset := Count
525 | Highest_RowIndex := A_Index
526 | }
527 | }
528 | if !SelectedRow
529 | SelectedRow := Highest_RowIndex
530 | return SelectedRow
531 | }
532 |
533 | reMIX_Level_1(FileName)
534 | { ; Fix Header
535 | MixFileObj := FileRead(FileName, "RAW")
536 | if !IsObject(MixFileObj)
537 | {
538 | msgbox "Failed to Read MIX Archive:`n" . FileName
539 | return false
540 | }
541 | Gui_ControlPanel_Progress.Value++
542 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fix Header
543 | NumPut("UInt", 0x00000000, MixFileObj)
544 | NumPut("UInt", MixFileObj.Size - 10 - (12 * NumGet(MixFileObj, 4, "UShort")), MixFileObj, 6)
545 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
546 | FromOldFilePath_SaveFuffer_ToNewFilePath(&FileName,&MixFileObj)
547 | Gui_ControlPanel_Progress.Value++
548 | return true
549 | }
550 |
551 | reMIX_Level_2(FileName)
552 | { ; Recover LMD File at Lookup Table -> Fix Header
553 | MixFileObj := FileRead(FileName, "RAW")
554 | if !IsObject(MixFileObj)
555 | {
556 | msgbox "Failed to Read MIX Archive:`n" . FileName
557 | return false
558 | }
559 | Gui_ControlPanel_Progress.Value++
560 |
561 | #IncludeAgain "Lib\GetMixInformations.ahk2" ; 4 progress value added after operation in this include
562 |
563 | if (LMDFile_OffsetfromBody.Length >= 1) and (LMDLookup.Length + InvalidInfo_Index.Length >= 1)
564 | {
565 | #IncludeAgain "Lib\LMDSelector_ChooseLMD.ahk2"
566 | NumPut("UInt", 0x366E051F, ; LMD ID
567 | "UInt", File_StartingOffsetfromBody,
568 | "UInt", NumGet(Area_Body_Ptr + File_StartingOffsetfromBody + 32, "UInt"), ; LMD Size
569 | Area_Info_Ptr + ( 12 * ( ( LMDLookup.Length >= 1 ) ? LMDLookup[LMDLookup.Length].Index : InvalidInfo_Index[1] ) ) ) ; LMD LookUp Table Ptr
570 | Gui_ControlPanel_Progress.Value++
571 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Eliminate LMD Duplicates
572 | if (LMDLookup.Length >= 2)
573 | {
574 | RandomNumber := Buffer(4)
575 | Loop LMDLookup.Length - 1
576 | {
577 | Loop {
578 | NumPut("UInt", Random(0x0,0xFFFFFFFF), RandomNumber)
579 | } Until !InBin(Area_Info_Ptr, Area_Info_Size, RandomNumber.Ptr, 4)
580 | NumPut("UInt", NumGet(RandomNumber,"UInt"),
581 | Area_Info_Ptr + ( 12 * LMDLookup[A_Index].Index ) ) ; LMD LookUp Table Ptr
582 | }
583 | }
584 | Gui_ControlPanel_Progress.Value++
585 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
586 | }
587 | else Gui_ControlPanel_Progress.Value += 2
588 |
589 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fix Header
590 | NumPut("UInt", 0x00000000, MixFileObj)
591 | NumPut("UInt", Area_Body_Size, MixFileObj, 6)
592 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
593 |
594 | FromOldFilePath_SaveFuffer_ToNewFilePath(&FileName,&MixFileObj)
595 | Gui_ControlPanel_Progress.Value++
596 | return true
597 | }
598 |
599 | reMIX_Level_3(FileName)
600 | { ; Reconstruct MIX Archive -> Recover LMD File at Lookup Table -> Fix Header
601 | MixFileObj := FileRead(FileName, "RAW")
602 | if !IsObject(MixFileObj)
603 | {
604 | msgbox "Failed to Read MIX Archive:`n" . FileName
605 | return false
606 | }
607 | reMIX_FileObj := Buffer(MixFileObj.Size)
608 | if !IsObject(reMIX_FileObj)
609 | {
610 | msgbox "Failed to Allocate Secondary Buffer:`n" . FileName
611 | return false
612 | }
613 | Gui_ControlPanel_Progress.Value++
614 |
615 | RedoreMIX:
616 | #IncludeAgain "Lib\GetMixInformations.ahk2" ; 4 progress value added after operation in this include
617 |
618 | /*
619 | Validate LookUpTable by Eliminating Garbage Informations:
620 | Case 1: one or more indexes have all their informations equal to each other. (ask user for help)
621 | Case 2: an index acting like a container was detected with one or more indexes inside it. (ask user for help)
622 | Case 3: an index overlaps with one or more indexes. (auto eliminate some garbage then ask user for help)
623 | */
624 | if (LookUpTable.Count >= 2)
625 | {
626 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Stage 3: let user manually eliminate duplicate and contained cases.
627 | if ((DuplicateList.Length + ContainerMap.Count) > 0)
628 | {
629 | ConflictSolver_ChangeMode(0)
630 | Gui_ConflictSolver_DuplicateLV.Opt("-Redraw")
631 | Gui_ConflictSolver_ContainerLV.Opt("-Redraw")
632 |
633 | Gui_ConflictSolver_DuplicateLV.Delete()
634 | Gui_ConflictSolver_DuplicateLV_Colors.UpdateProps() ; sync LV info to the CLV
635 | Gui_ConflictSolver_DuplicateLV_Colors.Clear()
636 | Gui_ConflictSolver_ContainerLV.Delete()
637 | Gui_ConflictSolver_ContainerLV_Colors.UpdateProps() ; sync LV info to the CLV
638 | Gui_ConflictSolver_ContainerLV_Colors.Clear()
639 |
640 | Loop DuplicateList.Length
641 | {
642 | IndexesGroup := ""
643 | For Key in DuplicateList[A_Index]
644 | {
645 | if (A_Index > 1)
646 | IndexesGroup .= " , "
647 | IndexesGroup .= Key . "(" . ID2Name(LookUpTable[Key].ID, &LMDMap) . ")"
648 | }
649 | Gui_ConflictSolver_DuplicateLV.Add("", , IndexesGroup, Format("0x{:08X}",LookUpTable[DuplicateList[A_Index][1]].OffsetfromBody), LookUpTable[DuplicateList[A_Index][1]].Size)
650 | Gui_ConflictSolver_DuplicateLV_Colors.UpdateProps() ; sync LV info to the CLV
651 | Gui_ConflictSolver_DuplicateLV_Colors.Row(A_Index, 0xFF0000, 0xFFFFFF)
652 | }
653 | Gui_ConflictSolver_DuplicateLV.ModifyCol(3, ,"Relative Offset From Body(" . Format("0x{:X}",Area_Body_Offset) . ")")
654 | Loop Gui_ConflictSolver_DuplicateLV.GetCount("Column") - 1
655 | Gui_ConflictSolver_DuplicateLV.ModifyCol(A_Index, "AutoHdr Center")
656 | For Key1 in ContainerMap
657 | {
658 | IndexesGroup := ""
659 | For Key2 in ContainerMap[Key1]
660 | {
661 | if (A_Index > 1)
662 | IndexesGroup .= " , "
663 | IndexesGroup .= Key2 . "(" . ID2Name(LookUpTable[Key2].ID, &LMDMap) . ")"
664 | }
665 | Gui_ConflictSolver_ContainerLV.Add("", Key1 . "(" . ID2Name(LookUpTable[Key1].ID, &LMDMap) . ")", IndexesGroup, Format("0x{:X}",LookUpTable[Key1].OffsetfromBody), LookUpTable[Key1].Size)
666 | Gui_ConflictSolver_ContainerLV_Colors.UpdateProps() ; sync LV info to the CLV
667 | Gui_ConflictSolver_ContainerLV_Colors.Row(A_Index, 0xFFD700, 0x000000)
668 | }
669 | Gui_ConflictSolver_ContainerLV.ModifyCol(3, ,"Relative Offset From Body(" . Format("0x{:X}",Area_Body_Offset) . ")")
670 | Loop Gui_ConflictSolver_ContainerLV.GetCount("Column") - 1
671 | Gui_ConflictSolver_ContainerLV.ModifyCol(A_Index, "AutoHdr Center")
672 | Gui_ConflictSolver_Comment.Value := DuplicateList.Length . " Duplicate Informations and " . ContainerMap.Count . " Contained Indexes Group have been Detected inside:`n" . FileName . "
673 | (
674 |
675 |
676 | What is the case about Duplicate Informations?
677 | * When two or more File ID's(found at the Lookup Table) have the same OffsetFromBody and FileSize. reMIXer will be confused on choosing which one among these File ID's is correct. File ID is important as this is the representation of the File's Name itself. So when a WRONG File ID was assigned for this File then the game will fail to recognize this file as it is interpreted with a different File Name(due to a different File ID). This could cause the Game to Crash if the Correct File ID wasn't Selected. So its important that you select which one of them is correct File ID.
678 |
679 | What is the case about Group Contained Conflict?
680 | * When the file territory of one or more File ID's(Contained) was found inside the file territory of another File ID(Container). The reMIXer cannot decide if the Container is a garbage information, or if the contained ones are garbage informations. Deleting a Correct File Information while Including Wrong File Informations will crash the game so be sure to correctly settle your checked/unchecked choices.
681 |
682 |
683 | To Settle all Duplicate Informations:
684 | 1) Double-Click a Specific Row you wish to Settle.
685 | 2) Select the File Index that you think is the GENUINE DATA among the Group.
686 | 2.1) The rest who are unselected inside this Group will be marked as GARBAGE/CORRUPTED DATA, deleting it in our Lookup Table.
687 | 2.2) Keep in mind that selecting a WRONG File Index(therefore getting deleted) will assign a garbage name to this file. The Game will CRASH if this file's real name is needed to be used.
688 | 3) Repeat this process to all fields marked with RED Color.
689 |
690 | To Settle all Group Contained Conflicts:
691 | * Providing a Row with Check Mark will treat only the Container File Index(Column 1) as GARBAGE/CORRUPTED DATA, deleting it in our Lookup Table.
692 | * Providing a Row with Uncheck Mark will treat all file indexes inside the Contained Group(Column 2) as GARBAGE/CORRUPTED DATA, deleting it in our Lookup Table.
693 | * Keep in mind that settling a WRONG Choice(therefore getting deleted). The Game will CRASH if this File Index were originally being used but is now missing.
694 |
695 |
696 | If you do not want to continue helping this tool settle the file information conflicts, click the Cancel MIX Reconstruction button. Keep in mind that will perform Level 2 Mode of Operation for this MIX Archive.
697 |
698 | Close this Window once you've finished settling the Duplicate and Container Conflicts.
699 | )"
700 |
701 | Gui_ConflictSolver_DuplicateLV.Opt("+Redraw")
702 | Gui_ConflictSolver_ContainerLV.Opt("+Redraw")
703 | global Gui_ConflictSolver_Status := True
704 | Gui_ConflictSolver.Show("Center")
705 | Loop
706 | {
707 | sleep 1
708 | if (Gui_ConflictSolver_Status == -1)
709 | {
710 | Gui_ControlPanel.Show()
711 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
712 | return Gui_ConflictSolver_Status
713 | }
714 | else if !Gui_ConflictSolver_Status
715 | break
716 |
717 | }
718 | Gui_ControlPanel.Show()
719 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
720 | Loop Gui_ConflictSolver_DuplicateLV.GetCount()
721 | {
722 | Key := Gui_ConflictSolver_DuplicateLV.GetText(A_Index,1)
723 | Loop Parse Gui_ConflictSolver_DuplicateLV.GetText(A_Index,2), ",", A_Space
724 | {
725 | if (A_LoopField != Key)
726 | {
727 | Key1 := A_LoopField
728 | #IncludeAgain "Lib\RemoveGarbageToLookupTable.ahk2"
729 | }
730 | }
731 | }
732 | Loop Gui_ConflictSolver_ContainerLV.GetCount()
733 | {
734 | if (Gui_ConflictSolver_ContainerLV.GetNext(A_Index-1,"C") == A_Index)
735 | { ; Remove File Index acting as a Container
736 | Key1 := Gui_ConflictSolver_ContainerLV.GetText(A_Index,1)
737 | #IncludeAgain "Lib\RemoveGarbageToLookupTable.ahk2"
738 | }
739 | else
740 | { ; Remove Contained File Indexes
741 | Loop Parse Gui_ConflictSolver_ContainerLV.GetText(A_Index,2), ",", A_Space
742 | {
743 | Key1 := A_LoopField
744 | #IncludeAgain "Lib\RemoveGarbageToLookupTable.ahk2"
745 | }
746 | }
747 | }
748 | }
749 | DuplicateList := "" ; free memory
750 | ContainerMap := "" ; free memory
751 | Gui_ControlPanel_Progress.Value++
752 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
753 |
754 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Stage 4: Analyze then Eliminate Garbage Informations
755 | OverlapMap_Garbage := Map()
756 | while (OverlapMap.Count > 0)
757 | {
758 | OverlappedFilesCount_Min := 0x7FFFFFFF
759 | OverlappedFilesCount_Max := 0
760 | For Key in OverlapMap
761 | {
762 | OverlappedFilesCount_Min := Min(OverlappedFilesCount_Min, OverlapMap[Key].Count)
763 | OverlappedFilesCount_Max := Max(OverlappedFilesCount_Max, OverlapMap[Key].Count)
764 | }
765 | if (OverlappedFilesCount_Min == OverlappedFilesCount_Max) ; We only have one Type of Garbage Level remaining among all indexes.
766 | break
767 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Collect All Files with the Highest Garbage Level
768 | OverlapMap_Garbage.Clear()
769 | For Key in OverlapMap
770 | {
771 | if (OverlapMap[Key].Count == OverlappedFilesCount_Max)
772 | OverlapMap_Garbage[Key] := OverlapMap[Key]
773 | }
774 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
775 | For Key1 in OverlapMap_Garbage
776 | { ; eliminate garbage files
777 | For Key2 in OverlapMap_Garbage[Key1]
778 | {
779 | if OverlapMap.Has(Key2)
780 | {
781 | if OverlapMap[Key2].Has(Key1) and (OverlapMap[Key2].Count < OverlappedFilesCount_Max)
782 | {
783 | if (OverlapMap[Key2].Count >= 2)
784 | OverlapMap[Key2].Delete(Key1)
785 | else OverlapMap.Delete(Key2)
786 | }
787 | }
788 | }
789 | ;Eliminate Garbage File
790 | LookUpTable.Delete(Key1)
791 | OverlapMap.Delete(Key1)
792 | }
793 | }
794 | Gui_ControlPanel_Progress.Value++
795 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
796 |
797 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Stage 5: Ask user to manually eliminate overlap garbage files
798 | if OverlapMap.Count
799 | {
800 | ConflictSolver_ChangeMode(1)
801 | Gui_ConflictSolver_OverlapLV.Opt("-Redraw")
802 |
803 | Gui_ConflictSolver_OverlapLV.Delete()
804 | Gui_ConflictSolver_OverlapLV_Colors.UpdateProps() ; sync LV info to the CLV
805 | Gui_ConflictSolver_OverlapLV_Colors.Clear()
806 |
807 | For Key1 in OverlapMap
808 | {
809 | IndexesGroup := ""
810 | For Key2 in OverlapMap[Key1]
811 | {
812 | if (A_Index > 1)
813 | IndexesGroup .= " , "
814 | IndexesGroup .= Key2 . "(" . ID2Name(LookUpTable[Key2].ID, &LMDMap) . ")"
815 | }
816 | Gui_ConflictSolver_OverlapLV.Add("", Key1 . "(" . ID2Name(LookUpTable[Key1].ID, &LMDMap) . ")", IndexesGroup, Format("0x{:X}",LookUpTable[Key1].OffsetfromBody), LookUpTable[Key1].Size)
817 | Gui_ConflictSolver_OverlapLV_Colors.UpdateProps() ; sync LV info to the CLV
818 | Gui_ConflictSolver_OverlapLV_Colors.Row(A_Index, 0xFF0000, 0xFFFF00)
819 | }
820 | Gui_ConflictSolver_OverlapLV.ModifyCol(3, ,"Relative Offset From Body(" . Format("0x{:X}",Area_Body_Offset) . ")")
821 | Loop Gui_ConflictSolver_OverlapLV.GetCount("Column") - 1
822 | Gui_ConflictSolver_OverlapLV.ModifyCol(A_Index, "AutoHdr Center")
823 |
824 | Gui_ConflictSolver_Comment.Value := OverlapMap.Count . " Overlapping File Informations have been Detected inside:`n" . FileName . "
825 | (
826 |
827 |
828 | What is the case about Overlapping Conflicts?
829 | * When the file territory of a certain File ID is overlapping one or more File ID's territory. The reMIXer cannot decide if the Overlapping file is a garbage information. Deleting a Correct File Information while Including Wrong File Informations will crash the game so be sure to correctly settle your checked/unchecked choices.
830 |
831 |
832 | To Settle all Overlapping Conflicts:
833 | * Providing a Row with Check Mark will treat the Overlapping Index(Column 1) as GARBAGE/CORRUPTED DATA, deleting it in our Lookup Table.
834 | * Providing a Row with UnCheck Mark will treat the Overlapping Index(Column 1) as a GENUINE DATA, maintaining it in our Lookup Table.
835 | * Keep in mind that settling a WRONG Choice(therefore getting deleted):
836 | - This tool could throw an error afterwards telling you to redo your settlement with the conflicts.
837 | - Else if this tool didn't detect your mistake then the Game will CRASH if this File Index were originally being used but is now missing.
838 |
839 |
840 | If you do not want to continue helping this tool settle the file information conflicts, click the Cancel MIX Reconstruction button. Keep in mind that will perform Level 2 Mode of Operation for this MIX Archive.
841 |
842 | Close this Window once all rows are marked as with green color.
843 | )"
844 | OutputFileSize := ComputeOutputFileSize(LookUpTable)
845 | ChangeBytesConflictText(OutputFileSize, MixFileObj.Size)
846 | Gui_ConflictSolver_OverlapLV.Opt("+Redraw")
847 | global Gui_ConflictSolver_Status := True
848 | PublicAPIValue({OverlapMap:OverlapMap,LookUpTable:LookUpTable,MaxSize:MixFileObj.Size})
849 | Gui_ConflictSolver.Show("Center")
850 | Loop
851 | {
852 | sleep 1
853 | if (Gui_ConflictSolver_Status == -1)
854 | {
855 | Gui_ControlPanel.Show()
856 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
857 | return Gui_ConflictSolver_Status
858 | }
859 | else if !Gui_ConflictSolver_Status
860 | break
861 |
862 | }
863 | Gui_ControlPanel.Show()
864 | Gui_ControlPanel_LV_Colors.UpdateProps() ; sync LV info to the CLV
865 | Loop Gui_ConflictSolver_OverlapLV.GetCount()
866 | {
867 | if (Gui_ConflictSolver_OverlapLV.GetNext(A_Index-1,"C") == A_Index)
868 | {
869 | Key := Gui_ConflictSolver_OverlapLV.GetText(A_Index,1)
870 | if LookUpTable.Has(Key)
871 | LookUpTable.Delete(Key)
872 | }
873 | }
874 | }
875 | OverlapMap := "" ; free memory
876 | Gui_ControlPanel_Progress.Value++
877 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
878 | }
879 | else Gui_ControlPanel_Progress.Value += 3
880 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
881 |
882 | reMIX_Area_Info_Offset := 0xA
883 | reMIX_Area_Info_Ptr := reMIX_FileObj.Ptr + Area_Info_Offset
884 | reMIX_Files_Count := LookUpTable.Count + 1 ; +1 for the LMD info region
885 | reMIX_Area_Info_Size := reMIX_Files_Count * 12 ; incase we detected a Corrupted LMD Info, then we add additional 12 bytes size to our new MIX Archive for our LMD Info
886 | reMIX_Area_Body_Offset := reMIX_Area_Info_Size + reMIX_Area_Info_Offset
887 | reMIX_Area_Body_Size := 0
888 | reMIX_Area_Body_MaxAllowedSize := reMIX_FileObj.Size - reMIX_Area_Body_Offset
889 | reMIX_Area_Body_Ptr := reMIX_FileObj.Ptr + reMIX_Area_Body_Offset
890 |
891 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ reconstruct the LMD Information Field
892 | if (LMDMap.Count > 0)
893 | { ; reconstruct LMD File
894 | reMIX_Seeker := reMIX_Area_Body_Ptr + 52 ; pointer to dictionary list
895 | For Key in LMDMap
896 | {
897 | StrPut(LMDMap[Key], reMIX_Seeker, "UTF-8")
898 | reMIX_Seeker += StrLen(LMDMap[Key]) + 1
899 | }
900 | File_Size := reMIX_Seeker - reMIX_Area_Body_Ptr
901 | DllCall("MSVCRT.dll\memcpy", "Ptr", reMIX_Area_Body_Ptr, "Ptr", LMD_Header_Pattern, "UInt", 32, "CDecl Ptr") ; set LMD header
902 | NumPut("UInt", File_Size, ; Size
903 | "UInt", 0x00000000, ; Padding
904 | "UInt", 0x00000000, ; Padding
905 | "UInt", 0x00000005, ; GameType == RA2/RA2YR/TS/TD
906 | "UInt", LMDMap.Count, ; Names Count
907 | reMIX_Area_Body_Ptr + 32)
908 | ; set LMD Info at Lookup Table
909 | NumPut("UInt", 0x366E051F, ; ID
910 | "UInt", 0x00000000, ; OffsetFromBody
911 | "UInt", File_Size, ; Size
912 | reMIX_Area_Info_Ptr)
913 | }
914 | else if (!LMDFile_OffsetfromBody.Length) ; No LMD File inside the Mix
915 | NumPut("UInt", 0x366E051F, "UInt", 0xFFFFFFFF, "UInt", 0x00000000, reMIX_Area_Info_Ptr)
916 | else
917 | { ; use the level 2 method of recovering LMD File
918 | Seeker := Area_Info_Ptr
919 | reMIX_Seeker := reMIX_Area_Info_Ptr
920 | #IncludeAgain "Lib\LMDSelector_ChooseLMD.ahk2"
921 | File_ID := 0x366E051F ; LMD ID
922 | File_Ptr := Area_Body_Ptr + File_StartingOffsetfromBody
923 | File_Size := NumGet(File_Ptr+32, "UInt")
924 | #IncludeAgain "Lib\MigrateFileToNewMixFile.ahk2"
925 | }
926 | LMDMap := "" ; Free Memory
927 | Gui_ControlPanel_Progress.Value++
928 | ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~
929 |
930 | LookUpTable_Count := LookUpTable.Count
931 | if IsObject(LookUpTable_Buffer)
932 | LookUpTable_Buffer.Size := LookUpTable_Count * 2
933 | else LookUpTable_Buffer := Buffer(LookUpTable_Count * 2)
934 | Seeker := LookUpTable_Buffer.Ptr
935 | For Key in LookUpTable
936 | {
937 | NumPut("UShort", Key, Seeker)
938 | Seeker += 2
939 | }
940 | LookUpTable := "" ; Free Memory
941 |
942 | starttime := A_TickCount
943 | FileSize := DllCall(ReconstructMIX,
944 | "Ptr" , MixFileObj.Ptr,
945 | "Ptr" , reMIX_FileObj.Ptr,
946 | "UInt" , reMIX_FileObj.Size,
947 | "Ptr" , LookUpTable_Buffer.Ptr,
948 | "UShort", LookUpTable_Count,
949 | "CDecl UInt")
950 | if !FileSize
951 | {
952 | if (MsgBox("MIX Archive reconstruction has encountered an error:`n" . FileName . "`n`nThis happens when you wrongfully settled file conflicts at the Conflict Solver Interface.`n`nTry to reMIX this file again and be sure to properly settle the file conflicts.`n`nWould you like to redo the conflict settlement?", "reMIXer Error", 20) == "Yes")
953 | goto RedoreMIX
954 | return false
955 | }
956 | reMIX_FileObj.Size := FileSize ; shrink the mix file
957 | Gui_ControlPanel_Progress.Value++
958 |
959 | FromOldFilePath_SaveFuffer_ToNewFilePath(&FileName,&reMIX_FileObj)
960 | Gui_ControlPanel_Progress.Value++
961 | return true
962 | }
963 |
964 | ID2Name(ID, &LMDMap)
965 | {
966 | return (LMDMap.Has(ID) ? LMDMap[ID] : Format("{:08X}",ID))
967 | }
968 |
969 | FromOldFilePath_SaveFuffer_ToNewFilePath(&Path,&Mix_Buffer)
970 | {
971 | SplitPath Path, &OutFileName, &OutDir
972 | FilePath := OutDir . "\reMIXed_" . OutFileName
973 | if FileExist(FilePath)
974 | {
975 | Loop
976 | {
977 | FilePath := OutDir . "\reMIXed" . A_Index . "_" . OutFileName
978 | if !FileExist(FilePath)
979 | break
980 | }
981 | }
982 | FileAppend Mix_Buffer, FilePath, "RAW"
983 | }
984 |
985 | GetLMDStatus(&MixFileObj)
986 | {
987 | #IncludeAgain "Lib\GetMixInformations.ahk2" ; 4 progress value added after operation in this include
988 | if LookUpTable.Count
989 | {
990 | RecoveryPercentage := 0
991 | For Key in LookUpTable
992 | {
993 | if LMDMap.Has(LookUpTable[Key].ID)
994 | RecoveryPercentage++
995 | }
996 | RecoveryPercentage := 100.00 * RecoveryPercentage / LookUpTable.Count
997 | }
998 | else RecoveryPercentage := 0
999 | if !LMDFile_OffsetfromBody.Length or (RecoveryPercentage == 0) ; no LMD file detected
1000 | return "Unrecoverable"
1001 | else if (LMDLookup.Length >= 1) ; LMD is considered Healthy if we found its info
1002 | {
1003 | LMDStatus := Format("{:.2f}", RecoveryPercentage) . "% Recoverable"
1004 | Loop LMDLookup.Length
1005 | {
1006 | if LMDLookup[A_Index].isHealthy
1007 | LMDStatus := "Healthy"
1008 | }
1009 | return LMDStatus
1010 | }
1011 | else if (InvalidInfo_Index.Length >= 1)
1012 | return Format("{:.2f}", RecoveryPercentage) . "% Recoverable"
1013 | else return RecoveryPercentage Format("{:.2f}", RecoveryPercentage) . "% Reconstructable"
1014 | }
1015 |
1016 | GetMixHeaderStatus(&MixFileObj)
1017 | {
1018 | EncryptionKey := NumGet(MixFileObj, "UShort")
1019 | if (EncryptionKey)
1020 | return Format("Unknown 0x{:X}", EncryptionKey)
1021 | return (NumGet(MixFileObj, 6, "UInt") == (MixFileObj.Size - 10 - (12 * NumGet(MixFileObj, 4, "UShort")))) ? "Healthy" : "Recoverable"
1022 | }
1023 |
1024 | GetColorFromStatus(Status,&Color_BG,&Color_TX)
1025 | {
1026 | Switch
1027 | {
1028 | case (Status == "Healthy"):
1029 | Color_BG := 0x00FF7F
1030 | Color_TX := 0x000000
1031 | case InStr(Status, "Reconstructable",True):
1032 | Color_BG := 0xFF8B00
1033 | Color_TX := 0x000000
1034 | case InStr(Status, "Recoverable",True):
1035 | Color_BG := 0xFFD700
1036 | Color_TX := 0x000000
1037 | default:
1038 | Color_BG := 0xFF0000
1039 | Color_TX := 0xFFFFFF
1040 | }
1041 | }
1042 |
1043 | PercentageToScreenWidth(Percentage)
1044 | {
1045 | return Percentage * (A_ScreenWidth / 100)
1046 | }
1047 |
1048 | PercentageToScreenHeight(Percentage)
1049 | {
1050 | return Percentage * (A_ScreenHeight / 100)
1051 | }
1052 |
1053 | #include "Lib\InBin.ahk2"
1054 | #include "Lib\StringToCRC32.ahk2"
--------------------------------------------------------------------------------