├── 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" --------------------------------------------------------------------------------