From 644593bfb93aa3389162862d29180f64ce0bbb6d Mon Sep 17 00:00:00 2001 From: umageddon Date: Thu, 21 Apr 2022 01:23:15 -0600 Subject: [PATCH] Add files via upload --- JSON.ahk | 730 ++++++++++++++++++++++------------------------------ namDHC.ahk | 240 ++++++++--------- threads.ahk | 336 ++++++++++++++---------- 3 files changed, 610 insertions(+), 696 deletions(-) diff --git a/JSON.ahk b/JSON.ahk index ca89b57..9575019 100644 --- a/JSON.ahk +++ b/JSON.ahk @@ -1,427 +1,309 @@ -; JSON for AutoHotkey -; Copyright (c) 2018 Kurt McKee -; The code is licensed under the terms of the MIT license. -; https://github.com/kurtmckee/ahk_json - -; VERSION = "1.0" -; ------------------------------------------------------- -json_escape(blob) -{ - hexadecimal := "0123456789abcdef" - - escapes := {} - escapes["`b"] := "\b" - escapes["`f"] := "\f" - escapes["`n"] := "\n" - escapes["`r"] := "\r" - escapes["`t"] := "\t" - escapes["/"] := "\/" - escapes["\"] := "\\" - escapes[""""] := "\""" - - - loop, % strlen(blob) - { - character := substr(blob, a_index, 1) - value := ord(character) - - ; Use simple escapes for reserved characters. - if (instr("`b`f`n`r`t/\""", character)) - { - escaped_blob .= escapes[character] - } - - ; Allow ASCII characters through without modification. - else if (value >= 32 and value <= 126) - { - escaped_blob .= character - } - - ; Use Unicode escapes for everything else. - else - { - hex1 := substr(hexadecimal, ((value & 0xF000) >> 12) + 1, 1) - hex2 := substr(hexadecimal, ((value & 0xF00) >> 8) + 1, 1) - hex3 := substr(hexadecimal, ((value & 0xF0) >> 4) + 1, 1) - hex4 := substr(hexadecimal, ((value & 0xF) >> 0) + 1, 1) - escaped_blob .= "\u" . hex1 . hex2 . hex3 . hex4 - } - } - - return escaped_blob -} - -json_unescape(blob) -{ - escapes := {} - escapes["b"] := "`b" - escapes["f"] := "`f" - escapes["n"] := "`n" - escapes["r"] := "`r" - escapes["t"] := "`t" - escapes["/"] := "/" - escapes["\"] := "\" - escapes[""""] := """" - - - index := 1 - loop - { - if (index > strlen(blob)) - { - break - } - - character := substr(blob, index, 1) - next_character := substr(blob, index + 1, 1) - if (character != "\") - { - unescaped_blob .= character - } - else if (instr("bfnrt/\""", next_character)) - { - unescaped_blob .= escapes[next_character] - index += 1 - } - else if (next_character == "u") - { - unicode_character := chr("0x" . substr(blob, index + 2, 4)) - unescaped_blob .= unicode_character - index += 5 - } - - index += 1 - } - - return unescaped_blob -} - -json_get_object_type(object) -{ - ; Identify the object type and return either "dict" or "list". - object_type := "list" - if (object.length() == 0) - { - object_type := "dict" - } - for key in object - { - ; The current AutoHotkey list implementation will loop through its - ; indexes in order from least to greatest. If the object can be - ; represented as a list, each key will match the a_index variable. - ; However, if it is a sparse list (that is, if it has non-consective - ; list indexes) then it must be represented as a dict. - if (key != a_index) - { - object_type := "dict" - } - } - - return object_type -} - -toJSON(info) -{ - ; Differentiate between a list and a dictionary. - object_type := json_get_object_type(info) - - for key, value in info - { - ; Only include a key if this is a dictionary. - if (object_type == "dict") - { - escaped_key := json_escape(key) - blob .= """" . escaped_key . """: " - } - - if (isobject(value)) - { - blob .= toJSON(value) . ", " - } - else - { - escaped_value := json_escape(value) - blob .= """" . escaped_value . """, " - } - } - - ; Remove the final trailing comma. - if (substr(blob, -1, 2) == ", ") - { - blob := substr(blob, 1, -2) - } - - ; Wrap the string in brackets or braces, as appropriate. - if (object_type == "list") - { - blob := "[" . blob . "]" - } - else - { - blob := "{" . blob . "}" - } - - return blob -} - -fromJSON(blob) +; +; cJson.ahk 0.4.1 +; Copyright (c) 2021 Philip Taylor (known also as GeekDude, G33kDude) +; https://github.com/G33kDude/cJson.ahk +; +; MIT License +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to deal +; in the Software without restriction, including without limitation the rights +; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +; copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in all +; copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +; SOFTWARE. +; + +class JSON { - - blob_length := strlen(blob) - index_left := 0 - index_right := 0 - - ; Identify the object type. - loop, % blob_length - { - index_left += 1 - - if (substr(blob, a_index, 1) == "[") - { - object_type := "list" - info := [] - break - } - else if (substr(blob, a_index, 1) == "{") - { - object_type := "dict" - info := {} - break - } - } - - ; Extract all key/value pairs. - loop, % blob_length - { - ; Extract the key. - ; Use an integer key if this is a list object. - if (object_type == "list") - { - key := info.length() + 1 - } - else - { - ; Find the left side of the key. - loop, % blob_length - { - index_left += 1 - - if (substr(blob, index_left, 1) == """") - { - break - } - } - - index_right := index_left - - ; Find the right side of the key. - loop, % blob_length - { - index_right += 1 - - ; Skip escaped characters, in case they are quotation marks. - if (substr(blob, index_right, 1) == "\") - { - index_right += 1 - } - else if (substr(blob, index_right, 1) == """") - { - break - } - } - - ; Store the key. - escaped_key := substr(blob, index_left + 1, index_right - index_left - 1) - key := json_unescape(escaped_key) - } - - ; Pass over whitespace and any colons that separate key-value pairs. - index_left := index_right + 1 - loop, % blob_length - { - index_left += 1 - - if (not instr("`b`f`n`r`t :", substr(blob, index_left, 1))) - { - break - } - } - - ; If the value isn't a string, adjust the left index to include - ; the beginning of the literal, dictionary, or list. - depth := 0 - in_string := true - value_type := "str" - index_right := index_left + 1 - if (substr(blob, index_left, 1) != """") - { - in_string := false - value_type := "literal" - if (substr(blob, index_left, 1) == "{") - { - depth := 1 - value_type := "dict" - } - else if (substr(blob, index_left, 1) == "[") - { - depth := 1 - value_type := "list" - } - - index_left -= 1 - } - - ; Find the right edge of the value. - ; - ; The loop will isolate the entire value, whether it's a string, - ; list, dictionary, boolean, integer, float, or null. For example: - ; - ; * "abc" - ; * 123 - ; * true - ; * false - ; * null - ; * [123, {"abc": true}] - ; * {"a": [123, null]} - loop - { - if (index_right > blob_length) - { - return info - } - - if (in_string) - { - ; If the right index is passing through a string and the - ; closing quotation mark is encountered, flag that the index - ; is no longer in a string, and exit the loop if the value is - ; a string. - if (substr(blob, index_right, 1) == """") - { - in_string := false - if (value_type == "str") - { - break - } - } - ; If the right index encounters a backslash in a string, the - ; next character is guaranteed to still be in the string. Move - ; the right index forward an additional character in case - ; the escaped character is a quotation mark. - else if (substr(blob, index_right, 1) == "\") - { - index_right += 1 - } - } - - ; If the right index encounters a quotation mark but is not already - ; in a string, flag that the index is now passing through a string. - else if (substr(blob, index_right, 1) == """") - { - in_string := true - } - - ; If the value is a dictionary, keep track of the depth of any - ; nested dictionaries. If the final closing curly brace is found, - ; move the right index forward so that the right curly brace will - ; be included in the value and exit the loop. - else if (value_type == "dict") - { - ; If the value is a dictionary - if (substr(blob, index_right, 1) == "{") - { - depth += 1 - } - else if (substr(blob, index_right, 1) == "}") - { - depth -= 1 - index_right += 1 - if (depth == 0) - { - break - } - } - } - - ; If the value is a list, keep track of the depth of any nested - ; lists. If the final closing bracket is found, move the right - ; index forward so that the right bracket will be included in - ; the value and exit the loop. - else if (value_type == "list") - { - if (substr(blob, index_right, 1) == "[") - { - depth += 1 - } - else if (substr(blob, index_right, 1) == "]") - { - index_right += 1 - depth -= 1 - if (depth == 0) - { - break - } - } - } - - ; If the value is a literal, such as a boolean or integer, just - ; watch for any character that will indicate that the end of the - ; literal has been encountered. - else if (value_type == "literal") - { - if (instr("`b`f`n`r`t ,]}", substr(blob, index_right, 1))) - { - break - } - } - - index_right += 1 - } - - ; Extract the value, now that its left and right sides have been found. - value := substr(blob, index_left + 1, index_right - index_left - 1) - - ; Recursively parse dictionaries and lists. - if (value_type == "dict" or value_type == "list") - { - value := fromJSON(value) - } - ; Escape string values. - else if (value_type == "str") - { - value := json_unescape(value) - } - ; Convert boolean and null literals to booleans. - else if (value == "true") - { - value := true - } - else if (value == "false") - { - value := false - } - else if (value == "null") - { - value := false - } - - ; Save the key/value pair. - info[key] := value - - ; Move the index. - index_left := index_right + 1 - } - - return info + static version := "0.4.1-git-built" + + BoolsAsInts[] + { + get + { + this._init() + return NumGet(this.lib.bBoolsAsInts, "Int") + } + + set + { + this._init() + NumPut(value, this.lib.bBoolsAsInts, "Int") + return value + } + } + + EscapeUnicode[] + { + get + { + this._init() + return NumGet(this.lib.bEscapeUnicode, "Int") + } + + set + { + this._init() + NumPut(value, this.lib.bEscapeUnicode, "Int") + return value + } + } + + _init() + { + if (this.lib) + return + this.lib := this._LoadLib() + + ; Populate globals + NumPut(&this.True, this.lib.objTrue, "UPtr") + NumPut(&this.False, this.lib.objFalse, "UPtr") + NumPut(&this.Null, this.lib.objNull, "UPtr") + + this.fnGetObj := Func("Object") + NumPut(&this.fnGetObj, this.lib.fnGetObj, "UPtr") + + this.fnCastString := Func("Format").Bind("{}") + NumPut(&this.fnCastString, this.lib.fnCastString, "UPtr") + } + + _LoadLib32Bit() { + static CodeBase64 := "" + . "FLYQAQAAAAEwVYnlEFOB7LQAkItFFACIhXT///+LRUAIixCh4BYASAAgOcIPhKQAcMdFAvQAFADrOIN9DAAAdCGLRfQF6AEAQA+2GItFDIsAAI1I" + . "AotVDIkACmYPvtNmiRAg6w2LRRAAKlABwQAOiRCDRfQAEAViIACEwHW5AMaZiSBFoIlVpAEhRCQmCABGAAYEjQATBCSg6CYcAAACaRQLXlDHACIA" + . "DFy4AZfpgK0HAADGRfMAxAgIi1AAkwiLQBAQOcJ1RwATAcdFCuwCuykCHAyLRewAweAEAdCJRbACiwABQAiLVeyDAMIBOdAPlMCIAEXzg0XsAYB9" + . "EPMAdAuEIkXsfIrGgkUkAgsHu1sBJpgFu3uCmYlOiRiMTQSAvYGnAHRQx0Wi6Auf6AX5KJ/oAAQjhRgCn8dF5AJ7qQULgUGDauSEaqyDfeSwAA+O" + . "qYAPE6EsDaGhhSlSx0XgiyngqilO4AACRQyCKesnUyAgIVUgZcdF3EIgVMdERdiLItgF/Kgi2EcAAkUMgiKDRdyABBiAO0XcfaQPtoB5gPABhMAP" + . "hJ/AwIHCeRg5ReR9fOScGItFrMCNALCYiVVKnA2wmAGwZRlEXxfNDxPpgTjKE+nKQgSAIaIcgCEPjZ9C3NQLQOjUBf4oQNQAAkUMRNyhxCyQiVWU" + . "zSyQYRaUsRiYbivqC+scwwlgi1UQiVTgCOAEVKQkBIEIYBqVCDqtKAN/Q4ctDIP4AXUek0EBLg76FwBhnAIBKKEDBQYPhV7COqzAmIbkICAAgVXH" + . "RdDLKbjQBQcAB98pwynQAAETJQbCKekqJA4QodzFRgzMSwzMBQxfDEYM7swAASUGQwzHphiBsUMMYshLDMgFEl8MRgzIFwABJQZDDGRCDBiNSBAB" + . "D7aVg7+siwAoiUwkoSwMjy3N+TD//+kv5BKBLQV1liBCBk8FVsBJ6QRIBYgCdWlAAY1VgCUEVNQUwVzEIho3IhogAItViItFxAHAiI0cAioaD7cT" + . "ERoExAEFBgHQD7cAgGaFwHW36ZCiZ2LACyXABRcfJeYKwM8AASUGpmcuHIAVv9RGCgbkAAHjyeQPjEj6RP//ZJ4PhLXiFbzt6xW8P6+IC7wAASUG" + . "BMRv4uLhqGH7CAu0/6iIBbQXgAAVA3RUuCABuDtFqBh8pFpxXVNxfV9xA11xkgmLXfzJw5DOkLEaAgBwiFdWkIizUYoMMBQUcQDHQAjRAdjHQAxS" + . "DIAECIEEwCEOCJFBwABhH4P4IHQi5dgACnTX2AANdCLJ2AAJdLvYAHsPjIVygjxoBcdFoDIHVkWBj2AAqGMArGEAoYaM8AjQLkAYixWhAJDHRCQg" + . "4gFEJCCLIAAAjU2QwDMYjdRNoGAAFFABEEGWcAAf8gtwAOMMQFdxAIkUJED/0IPsJItAY0U+sN8N3w3fDd8N1wB9D6yEVARuEgGFEG9DCQFAg/gi" + . "dAq4YCj/xOm/EAqNRYDxYOEHAeAtaf7//4XAdPoX8wGf8AH/Cf8J/wn/CXXVADrFB0LPBZJplAjfVv2SCMQCFcICiIM4CP1jArCyZ4ABTxRPCk8K" + . "TwqR1wAsdRIqBelUcBFmkFkWhQl8C18MgCwJQQIxVbCJUAjDqlTDdQLzA1sPhfBFGTYovIVwwUGxIjK5kwB4lgDOfJQA/yj8KI1gkAIiKZ6NEQVf" + . "KV8pVimFaBED/EW00KbxAq8VrxWvFa8VYdcAXQ+EtpSP9imlkwNA2B/h+9kfFwr1AdXgi+RjArRhArpQFS8Kby8KLwovCtkfFioFgVzplgGACBkg" + . "XcUJegkfIDUXILQWIFJ1AkQ4D4VMYwPvNYB4ReCSA+DDkAOjBAgA6e8FSxRvDbQH/pEgNwVcD4WqF51NKQdxe+CAAYlV4LsCazsuizkGwATbAlzc" + . "Aqpd2wIv2wIv3AIv2wKqYtsCCNwCAdsCZtsCqgzcAtPbTW7bAgrcAqql2wJy2wIN3AJ32wIudNsCMR7ZAknbAnUPfIURTT7gA4ADsWVCz+nPwdcw" + . "AQADoNyJwuEBOhuIL34w2AA5fyLDAoORAlMBAdCD6DCFAwTpgKk1g/hAfi0B2ADAtwBGfx+LReAPtwAAicKLRQiLAEEAkAHQg+g3AXDgIGaJEOtF" + . "BVhmg1D4YH4tCDRmE+hXEQZ0Crj/AADpbQZEAAACQI1QAgAOiQAQg0XcAYN93BADD44WAD6DReAoAusmAypCBCoQjQpKAioIAEmNSAKJGk0AZhIA" + . "Ugh9Ig+FAP/8//+LRQyLEkgBJinIAXcMi0AQCIPoBAEp4GbHCgAMeLgAEADp3QUjBBYDSC10JIgGLw8IjrEDig85D4+foYAIx0XYAYInDIArIhSB" + . "A8dACIEnx0DmDAEDiSh1FIAWAWiKPjGIEDB1IxMghRXpjhELKTB+dQlJf2frCkcBdlCBd2vaCmtAyAAB2bsKgBn3AOMB0YnKi00IAIsJjXECi10I" + . "AIkzD7cxD7/OAInLwfsfAcgRANqDwNCD0v+LAE0MiUEIiVEMSck+fhoJGX6dRXCrEAQAAJCIBi4PhYalTSyGI2YPbsDAAADKZg9iwWYP1mSFUEAQ" + . "361BAYAI3VZYwGpBUAUAVNQBVOsAQotV1InQweAAAgHQAcCJRdQBQxVIAotVCIkKAcAbmIPoMImFTIXAD9tDAUXU3vmBErBACN7BhRTIMA7KMCKi" + . "SANldBJIA0UPHIVVACANMQMHFHUxVQk00MAA2gA00wA0lVEVNMZF00uBE0AEAY3KF+tAzAYIK3URhgxX0IhNMsRiH8KizEGM61Ani1XMh07DUU4B" + . "ENiJRcxYFb3HRSLIwTDHRcRCChOLhFXIqDHIg0XEQBgAxDtFzHzlgH0Q0wB0E0Mv20XIoaMwWAjrEUcCyUYiFeUoKyR0WCBN2JmJAN8Pr/iJ1g+v" + . "APEB/vfhjQwWk2FVJFHrHcYGBXVmCibYcApELgMAA3oMAqFqZXQPhasiGsAiGgA3i0XABQcXAAAAD7YAZg++0FEmBTnCdGQqy+1AgwxFwKAexgaE" + . "wHW6lA+2wIYAQAF0G6UPJ0N4oidDeOssQwMJABCLFeQWgoWJUAhCoUIBAItABKMCiYAUJP/Qg+wEgxcuT2UPhKqFF7yFF7wF6gyaFw6PF7yAF8YG" + . "mhf76I+JF9yHF0IBgxdBAYsXgpKrlG51f8dFIgOA6zSLRbgFEhMX0gcCF+tYrBa4oBZmBvWgFr3nEeDnEUIB4xFBAQnqEesFIguNZfRbMF5fXcNB" + . "AgUAIlUAbmtub3duX08AYmplY3RfAA0KCiALIqUBdHJ1ZQAAZmFsc2UAbgh1bGzHBVZhbHUAZV8AMDEyMzQANTY3ODlBQkMAREVGAFWJ5VNAg+xU" + . "x0X0ZreLAEAUjVX0iVQkIBTHRCQQIitEJKIMwUONVQzAAgjAAQ8AqaAF4HPDFhjHReSpAgVF6MMA7MMA8IMKcBCJReRgAuPOIgwYqItV9MAIIKQL" + . "HOQAghjhAI1N5IlMgw/fwQyBD8QDwjwgEAQnD2De0hCDNgl1MCEQcE7xBUAIi1UQi1JFAgTE62hmAgN1XGECElESu0AWf7lBBTnDGSjRfBWGAT0g" + . "AYCJQNCD2P99LvAajTRV4HEPiXAPMR4EJATooQAChcB0EYsETeBGA4kBiVEEAJCLXfzJw5CQAXAVg+xYZsdF7ikTH0XwIBYUARBNDAC6zczMzInI" + . "9xDiweoDNkopwYkCyhAHwDCDbfQBgSGA9GaJVEXGsAMJ4gL34pAC6AOJRQAMg30MAHW5jUJVoAH0AcABkAIQDYAJCGIRwwko/v//hpBACLMdYMdF" + . "+EIuBhrkRcAKRfjB4AQgAdCJRdgBAUAYwDlF+A+NRPAZAAsKzlEC2PEMRfTGRQDzAIN99AB5B2GQAAH3XfRQHEMM9KC6Z2ZmZkAM6nAJhPgCUnkp" + . "2InC/wyog23s8gzs8QymngNAwfkficopoAj0AYEGdaWAffMAdAYOQQMhA8dERaYtHXAnpsAAwA5gAtDGRYbrkCXiJotF5I0hjCDQAdAPtzBn5I3S" + . "DMEWAcgDOnWQOQgCQABmhcB1GSUBDGUmAQYQBQHrEKG8AnQDUIS8AnQHg0XkAQDrh5CAfesAD2aEoWbhH1XYMJnRLemSyiQuQBwhFYyj4gChwxTU" + . "xkXjgAvcgwvq3IIF1IQL3I8LCAKFC/sjAYoL44ILvAKBC7wCgQtC3IML4wB0D0oL65AYg0X48n1AEFIL8Nf9//9ySLosvz1iABNyQ2Aj6AWBD90A" + . "3RpdkC7YswGyDsdF4ONjACIbjUXoUCcwAZEH7KGIED3jQBWhAB1BIXXATCQYjU3YBUFCav8MQeVIFUEhCz8LPwvAATES0QAxBIsAADqJIEmfC3+f" + . "C58LnwufC58Lnws2O2Q9wAnmkgrSNjQKV0l8GIM1AStMfW6NRahoSib2kEBUD+s3gUN0IACLVbCLRfABwFSNHPBqDHSWDHGWE12xzg2hIcBs8CAQ" + . "wWzw1gEFA2YntzNzPvR60xMA7IN97AB5bYtMTeyPQY9BuDAQBCk60KpOvr4DpkHCBXWjB+ECwQJAQb4tAOtbX88GzwZfVa8GrwalhCPrQj5CEyeN" + . "Vb7WVuhnvxO/E7IT6AF8AyYUqWvpNbMqGJIGF3oFUIMimADpyXLcmAXpt5PdKQTkdVaiAxStA1wAVx0JHwYTBmMeBlEZBlxPHwYfBh8GaALpAR4G" + . "73vTaBMGCB8GHwYfBmYCYgAAgrEA6Z8CAACLRRAgiwCNUAEAcIkQBOmNAogID7cAZgCD+Ax1VoN9DEAAdBSLRQwAjEgAAotVDIkKZsdgAFwA6w0K" + . "3AJMF6ENTGYA6T0OwisJwoIKPGFuAOnbAQ1hFskCEQRhDTxhcgDpKnmOMGeJMAm8MHQAFOkXjjAFgAgPtgUABAAAAITAdCkRBjYfdgyGBX52B0K4" + . "ABMA6wW4gAIAoIPgAeszCBQYCBTCE4QFPaAAdw0awBc2bykwjgl1jQkDGw+3AMCLVRCJVCQIAQEKVCQEiQQk6DptgR4rwhHAJ8gRi1UhwAwSZokQ" + . "jRxFCAICBC+FwA+FOvwU//9TISJNIZDJwwCQkJBVieVTgwTsJIAQZolF2McARfAnFwAAx0UC+AE/6y0Pt0XYAIPgD4nCi0XwAAHQD7YAZg++ANCL" + . "RfhmiVRFQugBB2bB6AQBDoMARfgBg334A36gzcdF9APBDjOCIQAci0X0D7dcRZLoiiOJ2hAybfRAEBD0AHnHAl6LXfwBwic=" + static Code := false + if ((A_PtrSize * 8) != 32) { + Throw Exception("_LoadLib32Bit does not support " (A_PtrSize * 8) " bit AHK, please run using 32 bit AHK") + } + ; MCL standalone loader https://github.com/G33kDude/MCLib.ahk + ; Copyright (c) 2021 G33kDude, CloakerSmoker (CC-BY-4.0) + ; https://creativecommons.org/licenses/by/4.0/ + if (!Code) { + CompressedSize := VarSetCapacity(DecompressionBuffer, 3935, 0) + if !DllCall("Crypt32\CryptStringToBinary", "Str", CodeBase64, "UInt", 0, "UInt", 1, "Ptr", &DecompressionBuffer, "UInt*", CompressedSize, "Ptr", 0, "Ptr", 0, "UInt") + throw Exception("Failed to convert MCLib b64 to binary") + if !(pCode := DllCall("GlobalAlloc", "UInt", 0, "Ptr", 9092, "Ptr")) + throw Exception("Failed to reserve MCLib memory") + DecompressedSize := 0 + if (DllCall("ntdll\RtlDecompressBuffer", "UShort", 0x102, "Ptr", pCode, "UInt", 9092, "Ptr", &DecompressionBuffer, "UInt", CompressedSize, "UInt*", DecompressedSize, "UInt")) + throw Exception("Error calling RtlDecompressBuffer",, Format("0x{:08x}", r)) + for k, Offset in [33, 66, 116, 385, 435, 552, 602, 691, 741, 948, 998, 1256, 1283, 1333, 1355, 1382, 1432, 1454, 1481, 1531, 1778, 1828, 1954, 2004, 2043, 2093, 2360, 2371, 3016, 3027, 5351, 5406, 5420, 5465, 5476, 5487, 5540, 5595, 5609, 5654, 5665, 5676, 5725, 5777, 5798, 5809, 5820, 7094, 7105, 7280, 7291, 8610, 8949] { + Old := NumGet(pCode + 0, Offset, "Ptr") + NumPut(Old + pCode, pCode + 0, Offset, "Ptr") + } + OldProtect := 0 + if !DllCall("VirtualProtect", "Ptr", pCode, "Ptr", 9092, "UInt", 0x40, "UInt*", OldProtect, "UInt") + Throw Exception("Failed to mark MCLib memory as executable") + Exports := {} + for ExportName, ExportOffset in {"bBoolsAsInts": 0, "bEscapeUnicode": 4, "dumps": 8, "fnCastString": 2184, "fnGetObj": 2188, "loads": 2192, "objFalse": 5852, "objNull": 5856, "objTrue": 5860} { + Exports[ExportName] := pCode + ExportOffset + } + Code := Exports + } + return Code + } + _LoadLib64Bit() { + static CodeBase64 := "" + . "xrUMAQALAA3wVUiJ5RBIgezAAChIiU0AEEiJVRhMiUUAIESJyIhFKEggi0UQSIsABAWVAh0APosASDnCD0SEvABWx0X8AXrrAEdIg30YAHQtAItF" + . "/EiYSI0VQo0ATkQPtgQAZkUCGAFgjUgCSItVABhIiQpmQQ++QNBmiRDrDwAbICCLAI1QAQEIiRDQg0X8AQU/TQA/AT4QhMB1pQJ9iUWgEEiLTSAC" + . "Q41FoABJichIicHoRhYjAI4CeRkQaMcAIgoADmW4gVfpFgkAMADGRfuAZYFsUDBJgwNAIABsdVsADAEox0X0Amw1hBAYiwRF9IBMweAFSAGa0IBG" + . "sIALgAFQEIALGIPAAQANAImUwIgARfuDRfQBgH2Q+wB0EwEZY9AILRR8sgNWLIIPCEG4wlsBMQZBuHsBuw9gBESJj1+AfSgAdFBkx0XwjLvwgpsm" + . "mhyxu/DAXcMP5hvHXcjHRezCSqUGAidEQQLsSUGog33sAA8sjsqBL5hhLJQxZsfUReiMMeiCIV+AIa8xluiAMcMPH4gx6y+ZJkIglCZ5x0XkgiZo" + . "KMdF4Mwo4MIYvhpt8SjgwCjDD37AD8UogwRF5MAFMDtF5H0IkA+2wJDwAYTAuA+E6EDpQVwGkTBBmdyNiZxbUL1AAajgB+FoSpjoaJjkaP4fJQoc" + . "mTQK6f5DVIkK6epgAo3qEzjiE0Fsx0XcrCay3KIeihm/Jq8m3KAjXeMHSuAHCIOFGpCIGpAthBophhrWJCwsDesbp2YK5AlkCb0gewk6UC4Dv04t" + . "NItAGIP4AU51YTCAEAoQXB4gcReGA2Iw4wQGD4Wf4EMzYwVhswkYoAHgl2nH1EXYbC/YYicXAAR/L01tL9hgL+MH1xdnL+m0iwJpD21AA2QP1GwP" + . "utRiB6AABH8PbQ/UYA9V4wdgaQ8Pag8BZw/QtWwP0GIHKn8PcA/QYA8p4wfqFmgPk2JyMI00SAFACk1B48AQAExDgAZBColMJCDBNa1g+P//6WjE" + . "M8I1Bax1H2QFLDtiITs9SQUQAg+Fg6NtqEiNoJVw////4QSKYJoox0XMIhxIIxwuSIiLlXjAA4tFzAAVYAHATI0EABttHEHoD7cQUxzMkAAKBFBd" + . "AA+3AGaFwHWeVOmqUjzIHBXIEhHdbhUfFR8V7QbIEBXzA5338ANbPCoRb6AO7zMPTtoFDuzQBahI8XYPjET5RP//8VwPhN3iDMTl7AzE4gjwFO8M" + . "7wwNB/bEAAfzA7DwA1dzMZRyY+sBkskGvMIChs8GzwbOBha8wAbzA0bIBoNFwIFwAcA7RTB8kKyFOl2khX2vha+FqJFIgeLEAQxdw5AKAOyiDgAK" + . "VcCjMEEsjawkgBVCpI2zpJURJEiLhQthAKAbFLUASMdACNvyEZAJhaICAQpQAArTAAcRUXUBMSmD+CB01REtAQp0wi0BDXSvES0BCXScLQF7D4WO" + . "KcJUrweiB8dFUMIQKMdFWHQAYHIAiwWOA+E4AT9BowX1/tAAEMdEJEBTAkQkOAGCAI1VMEiJVCSqMIAAUIEAKJABICG3VEG58QFBkha6ogKJUMFB" + . "/9LwFzhQbGh/zxDPEM8QzxDPEM8QJwF9WA+EwvJHaQGF8IesgV4Bg/gidAq4IBDw/+lmEYEOoblgB8IeAOj3/f//hcB0+iIDAkUBAu8M7wzvDO8M" + . "l+8M7wwkAToVCsQQDwi3CAhSKMcLOsMLtAOIsgNJsDKLjQMsRWjESQL/YA1/Go8Njw2PDY8Njw0nAZgsdR1vB2MH6cLQC+dAkIwd1Qy6D58QnBCw" + . "OQIJtjmLVWhIiVAaCLPSfcoDkwVbD4W+ZUJ4PwX0M/LJcAD4dADTUkIQM8P7+TO10QD/M+yNVdDF8zPw/zP/M+AZwtjwM3DHhay07R8aPx8aHxof" + . "Gh8aHxonAV0PNoRh45803kdQKCfH+pkpJxUOMQLiJouVcQz1UA1wRCftMBgvDS8NLw0BJAH+tQAKdMJIi4XAAAAAAEiLAA+3AEBmg/gNdK8NkAlE" + . "dJwNSCx1JAdISAiNUAIFGokQg4UCrAAQAemq/v//gpANbl10Crj/AAC46T4NASoTggAJyAAJMGbHAAkBIwELSIuAVXBIiVAIuAALGADpAQo8A1ki" + . "D4WMEwUaUwUXiYWgAgkdBFiVggaALQc7CADpRFkEDTGFwHWEXYKCDA8/XA+F9gMhP7mEVnU0AAmCPIETiQJC5YA8IpYg6ccKL4Q6FCOqXBcjgBAj" + . "L5QRL5cRKjmQEWKUEQiXEfICVY8RZpQRDJcRq5ARblWUEQqXEWSQEXKUEQ11lxEdkBF0lBFCuJMR1sIBjxF1D4WFigWOmcHEFQAAx4WcAcvByw47" + . "gwyBBoARweAEiUeB/UIKT1MvfkJNAjkcfy/HB2IHxwMB0INk6DDpCemuo2sqCEBEfj9NAkZ/LJoKN6mJCutczQdgLwpmPAqmVyoKhHm1CNcpg0Io" + . "CAGDvcEAAw+OuIlAmkiDIggC6zrjB8J16QcQSI1K5wchitUjPkggPo0DExJQLmCXLJD7QAtFkkgmBynIBkiCFuMCQAhIg+hOBMs8dRcjpdcHbzEt" + . "xHQubj4PjgyKp+Q+iA+P9eCgx4WYwSDLh6YADxQGqMdAICCwDDx1IuMGoSTfooMGMHUPITjTCk1+cA4wD46JwdACOX9260yGKAC9AInQSMHgAkgB" + . "gNBIAcBJicBpDCkgNYuVYwwKoAdID6C/wEwBwGAP0AUISyPFTGYfbg5+jiVMUwgGAAAO4S4PheYD2BtIPmYP78DySIwPKsEUYQLyDxHgQBUGMQXA" + . "M5TEM+tsixKVYQGJ0MAbAdAB7MCJQgP4G5jAOwIG8AUNcADScAASBGYPKMgQ8g9eyjYHEEAIsPIPWME8CFwQFw8kTI5q6h9jAWV0ngJFuA+F+I9N" + . "/RCzAhRXImP/Ef8RxoWTDyoBKiFNkwEBTwdDB+syPQMr3HUf3gQfLUsRE68hhCEKOrI1jFRa6zqLlduxAMYbQZ8pnBtEER4xA4NfB18HfqDHhYiE" + . "IojHhYRVBxyLlVEBSygj4QCDAgIBi2IAOyEyBnzWgL2iD3Qq61kh4BfJUCONUQMQIxoilOsolwJIgxoPKvIFePIPWb0k+R3BpdU6i0FSREiYSA+v" + . "OTjr8jg6AwV1vwawBqEDvwalugYMtyIDAFNToQ98oPh0D4XfkhOAlROMUouyAJAJjRXSEAOAD7YEEGYPvkEK6ZgDOcIlr0taBZ1moQQL8BYWBYAU" + . "BYTAdZcAD7YFUuT//4T4wHQdyQqoUtI/FRFkhcwVDgMHV0sF/CI2Q1AIiwXu0QCJwf/SBVMPq/+G+GYPhdMJUQ9FfCIPTItFfN3SCeewAv8O+w5b" + . "/zz3DmhFfAG1BJu0BJAOoLmQDmjjnw5MYZ4OBKMGbZgO8lQHkw7kggGWDsFBLzP4bg+FpZIOeKESBkmLRXjSCQOfDmWXDgeSDut0bw5lDnhbYA6D" + . "BLoxJ2MOo+wLVSv4yOMLQ+oLNeoL6wUhUgdIgcQwsAldwz6QBwCkKQ8ADwACACJVAG5rbm93bl9PAGJqZWN0XwANCgoQCSLVAHRydWUAAGZhbHNl" + . "AG4IdWxs5wJWYWx1AGVfADAxMjM0ADU2Nzg5QUJDAERFRgBVSInlAEiDxIBIiU0QAEiJVRhMiUUgaMdF/ANTRcBREVsoAEiNTRhIjVX8AEiJVCQo" + . "x0QkEiDxAUG5MSxJicgDcRJgAk0Q/9BIx0RF4NIAx0XodADwwbQEIEiJReDgAFOJAaIFTItQMItF/IpIEAVA0wJEJDiFAOIwggCNVeBGB8BXQAcH" + . "ogdiFXGWTRBB/9Lz0QWE73UeogaBl8IYYAYT5ADRGOtgpwIDdVODtQEBDIBIOdB9QG4V1AK68Bp/Qhs50H9l4FNF8Q/YSXCIUwfooUE2hcB0D6AB" + . "2LDuBVADUjAGEJBIg+xmgBge8xXsYPEV5BVmo7IREAWJRfigFhSABACLTRiJyrjNzATMzDBTwkjB6CAgicLB6gMmXinBAInKidCDwDCDzLQAbfwB" + . "icKLRfwASJhmiVRFwIsARRiJwrjNzMwAzEgPr8JIwegAIMHoA4lFGIMAfRgAdalIjVUDAIQArEgBwEgB0ABIi1UgSYnQSACJwkiLTRDoAQD+//+Q" + . "SIPEYAhdw5AGAFVIieUASIPscEiJTRAASIlVGEyJRSAQx0X8AAAA6a4CAAAASItFEEiLRFAYA1bB4AUBV4k0RdABD2MAYQEdQDAASDnCD42aAQBg" + . "AGbHRbgCNAAaQAEAUEXwxkXvAEhAg33wAHkIAAoBAEj3XfDHRegUgwBfAJTwSLpnZgMAgEiJyEj36kgArgDB+AJJichJwWD4P0wpwAG8gQngBgIB" + . "PABrKcFIicoAidCDwDCDbegVgo3og42QmCdIwflSPwAbSCmBXfACR3WAgIB97wB0EIEigYMhx0RFkC0AgKEGkIIHhKGJRcDGRSDnAMdF4IGJi0Uy" + . "4IAMjRQBcQEPD7cKEAQJDAEJGEgByAAPtwBmOcJ1b4EPFQBmhcB1HokLi4AXhQsGgDIB6zqTGgR0IlMNdAqDReAQAelm/0B2gH3nkAAPhPYCVkUg" + . "wH6JwC4QuMBkAOkBQAFlCmw4AWyMysMKhWrIqMZF38A52MM52IYb/sjFOYIE0DmNCsU5xwXLOb7fwjlRDcE5UQ3BOdjGORDfAHQSzTjrIIMsRfwA" + . "cgg5IAI5O/0M//+ApEA6g8RwXWLDwruB7JABBIS8SGvEdsAB6MQB8MEBwLLgAgUCwPIPEADyD6IRQIXHRcCECMjEAXrQwgGNgGdAioADASNIAIsF" + . "hOb//0iLoABMi1AwQAN2QQMQx0QkQAMNRCQ4hQICiwAfiVQkMMHtlQECKEAGIAEQQbnBBwpBwi26QgWJwUH/sNJIgcQBF/B3QOl3fwAXABmgeKNs" + . "gSEACOReD0yJm39veW+4MOAHKRzQgyyTv2+pbw+Feg9gOWEIIwhgb8AtAOkegF8T34IfE9qCx0XsCSEu61DgARgAdDYLi6oAC+xCAUyNBAIzYlRg" + . "K41IQAFhOQpBQQBlZokQ6w/hU4sQAI1QAQEBiRCDWEXsARQJR2OO5VRAWyc85Dsg6TsDExyvD2aAxwAiAOleBEOAKcgP6UpjAhAhDYP4KCJ1ZmMI" + . "GXIIXADT7hdcDuYDTw7SYwJEDk5cXw5fDsgF6XNQDl8dSg4IXw5fDsYFYgDp2gBQDuxk5EMODF8OXw5hxgVmAOmNwwsqB3l9KgcKLwcvBy8HLwfi" + . "Am5IAOkaLwfpBioHDR8vBy8HLwcvB+ICcgDptqcwTy0HkzMBJAcJLwcPLwcvBy8H4gJ0AOk0BS8H6aFXD7YFmdZA//+EwHQr1wcfRHYNxwB+dgcT" + . "ZwVB4jqD4AHrNqkCGoWpAhTFAD2gAHd9A31ABnxfDV8NXg3vAuECdbPvAtQHD7dRUPFyGCBUUInB6IZxCDTDBB43zwRgAGADEo9MAQhFEANxT0IN" + . "hcAPhab736BtXwnYQT4EQaggJE71TQtgWdVriQBrjQVC8wdwBVBZxKjrMg+3RXAQg+AP0qzAWlBTtrAAZg++kqiSXugRAjBmwegEEQTRgIN9gPwD" + . "fsjHRfhwOwgA6z9TCiWLRfjASJhED7dE4HwOC5hEicJfD+BbbfjQBDD4AHm7JVr1Cw==" + static Code := false + if ((A_PtrSize * 8) != 64) { + Throw Exception("_LoadLib64Bit does not support " (A_PtrSize * 8) " bit AHK, please run using 64 bit AHK") + } + ; MCL standalone loader https://github.com/G33kDude/MCLib.ahk + ; Copyright (c) 2021 G33kDude, CloakerSmoker (CC-BY-4.0) + ; https://creativecommons.org/licenses/by/4.0/ + if (!Code) { + CompressedSize := VarSetCapacity(DecompressionBuffer, 4249, 0) + if !DllCall("Crypt32\CryptStringToBinary", "Str", CodeBase64, "UInt", 0, "UInt", 1, "Ptr", &DecompressionBuffer, "UInt*", CompressedSize, "Ptr", 0, "Ptr", 0, "UInt") + throw Exception("Failed to convert MCLib b64 to binary") + if !(pCode := DllCall("GlobalAlloc", "UInt", 0, "Ptr", 11168, "Ptr")) + throw Exception("Failed to reserve MCLib memory") + DecompressedSize := 0 + if (DllCall("ntdll\RtlDecompressBuffer", "UShort", 0x102, "Ptr", pCode, "UInt", 11168, "Ptr", &DecompressionBuffer, "UInt", CompressedSize, "UInt*", DecompressedSize, "UInt")) + throw Exception("Error calling RtlDecompressBuffer",, Format("0x{:08x}", r)) + OldProtect := 0 + if !DllCall("VirtualProtect", "Ptr", pCode, "Ptr", 11168, "UInt", 0x40, "UInt*", OldProtect, "UInt") + Throw Exception("Failed to mark MCLib memory as executable") + Exports := {} + for ExportName, ExportOffset in {"bBoolsAsInts": 0, "bEscapeUnicode": 16, "dumps": 32, "fnCastString": 2624, "fnGetObj": 2640, "loads": 2656, "objFalse": 7632, "objNull": 7648, "objTrue": 7664} { + Exports[ExportName] := pCode + ExportOffset + } + Code := Exports + } + return Code + } + _LoadLib() { + return A_PtrSize = 4 ? this._LoadLib32Bit() : this._LoadLib64Bit() + } + + Dump(obj, pretty := 0) + { + this._init() + if (!IsObject(obj)) + throw Exception("Input must be object") + size := 0 + DllCall(this.lib.dumps, "Ptr", &obj, "Ptr", 0, "Int*", size + , "Int", !!pretty, "Int", 0, "CDecl Ptr") + VarSetCapacity(buf, size*2+2, 0) + DllCall(this.lib.dumps, "Ptr", &obj, "Ptr*", &buf, "Int*", size + , "Int", !!pretty, "Int", 0, "CDecl Ptr") + return StrGet(&buf, size, "UTF-16") + } + + Load(ByRef json) + { + this._init() + + _json := " " json ; Prefix with a space to provide room for BSTR prefixes + VarSetCapacity(pJson, A_PtrSize) + NumPut(&_json, &pJson, 0, "Ptr") + + VarSetCapacity(pResult, 24) + + if (r := DllCall(this.lib.loads, "Ptr", &pJson, "Ptr", &pResult , "CDecl Int")) || ErrorLevel + { + throw Exception("Failed to parse JSON (" r "," ErrorLevel ")", -1 + , Format("Unexpected character at position {}: '{}'" + , (NumGet(pJson)-&_json)//2, Chr(NumGet(NumGet(pJson), "short")))) + } + + result := ComObject(0x400C, &pResult)[] + if (IsObject(result)) + ObjRelease(&result) + return result + } + + True[] + { + get + { + static _ := {"value": true, "name": "true"} + return _ + } + } + + False[] + { + get + { + static _ := {"value": false, "name": "false"} + return _ + } + } + + Null[] + { + get + { + static _ := {"value": "", "name": "null"} + return _ + } + } } -isJSONValid(string) -{ - static doc := ComObjCreate("htmlfile") - , __ := doc.write("") - , parse := ObjBindMethod(doc.parentWindow.JSON, "parse") - try %parse%(string) - catch - return false - return true -} \ No newline at end of file diff --git a/namDHC.ahk b/namDHC.ahk index da48ff2..3932324 100644 --- a/namDHC.ahk +++ b/namDHC.ahk @@ -4,11 +4,11 @@ detectHiddenWindows On setTitleMatchmode 3 SetBatchLines, -1 -SetKeyDelay, -1, -1 -SetMouseDelay, -1 -SetDefaultMouseSpeed, 0 -SetWinDelay, -1 -SetControlDelay, -1 +;SetKeyDelay, -1, -1 +;SetMouseDelay, -1 +;SetDefaultMouseSpeed, 0 +;SetWinDelay, -1 +;SetControlDelay, -1 SetWorkingDir %a_ScriptDir% /* @@ -68,6 +68,8 @@ v1.07 - Fixed some race conditions - Lots of code change - GUI changes n' stuff + - CHanges JSON library again (hopefully last time) + - Having issues with script pausing/hanging after cancel command is sent to thread */ @@ -75,7 +77,6 @@ v1.07 #Include ClassImageButton.ahk #Include ConsoleClass.ahk #Include JSON.ahk -#Include JSON2.ahk ; Default global values ; --------------------- @@ -91,7 +92,7 @@ APP_RUN_JOB_NAME := APP_MAIN_NAME " - Job" APP_RUN_CHDMAN_NAME := APP_RUN_JOB_NAME " - chdman" APP_RUN_CONSOLE_NAME := APP_RUN_JOB_NAME " - Console" TIMEOUT_SEC := 25 -WAIT_TIME_CONSOLE_SEC := 15 +WAIT_TIME_CONSOLE_SEC := 1 JOB_QUEUE_SIZE := 3 JOB_QUEUE_SIZE_LIMIT := 10 OUTPUT_FOLDER := a_workingDir @@ -106,9 +107,9 @@ APP_VERBOSE_WIN_POS_Y := 150 APP_MAIN_WIN_POS_X := 800 APP_MAIN_WIN_POS_Y := 100 -globals := {} -; Read ini to write over global variables if changed previously +; Read ini to write over globals if changed previously +;------------------------------------------------------------- ini("read" ,["JOB_QUEUE_SIZE","OUTPUT_FOLDER","SHOW_JOB_CONSOLE","SHOW_VERBOSE_WINDOW","PLAY_SONG_FINISHED","REMOVE_FILE_ENTRY_AFTER_FINISH" ,"APP_MAIN_WIN_POS_X","APP_MAIN_WIN_POS_Y","APP_VERBOSE_WIN_WIDTH","APP_VERBOSE_WIN_HEIGHT","APP_VERBOSE_WIN_POS_X","APP_VERBOSE_WIN_POS_Y","CHECK_FOR_UPDATES_STARTUP"]) @@ -129,16 +130,16 @@ if ( a_args[1] == "threadMode" ) { ; Kill all processes so only one instance is running -;--------------------------------------------------- +;------------------------------------------------------------- killAllProcess() ; Set working job variables -; ---------------------------- +;------------------------------------------------------------- job := {workTally:{}, msgData:[], availPSlots:[], workQueue:[], scannedFiles:{}, queuedMsgData:[], InputExtTypes:[], OutputExtType:[], selectedOutputExtTypes:[], selectedInputExtTypes:[]} ; Set GUI variables -; ----------------- +;------------------------------------------------------------- GUI := { chdmanOpt:{}, dropdowns:{job:{}, media:{}}, buttons:{normal:[], hover:[], clicked:[], disabled:[]}, menu:{namesOrder:[], File:[], Settings:[], About:[]} } GUI.dropdowns.job := { create: {pos:1,desc:"Create CHD files from media"} ,extract: {pos:2,desc:"Extract images from CHD files"} @@ -153,7 +154,7 @@ GUI.buttons.cancel := {normal:[0, 0xFFFC6D62, "", "White", 3], hover:[0, 0xFFff8 GUI.buttons.start := {normal:[0, 0xFF74b6cc, "", 0xFF444444, 3], hover:[0, 0xFF84bed1, "", "White", 3], clicked:[0, 0xFFa5d6e6, "", "White", 3], disabled:[0, 0xFFd3dde0, "", 0xFF888888, 3] } ; Set menu variables -; ------------------- +;------------------------------------------------------------- GUI.menu["namesOrder"] := ["File", "Settings", "About"] GUI.menu.File[1] := {name:"Quit", gotolabel:"quitApp", saveVar:""} GUI.menu.About[1] := {name:"About", gotolabel:"menuSelected", saveVar:""} @@ -165,7 +166,7 @@ GUI.menu.Settings[5] := {name:"Play a sound when finished jobs", gotolabel:"m GUI.menu.Settings[6] := {name:"Remove file entry from list on success", gotolabel:"menuSelected", saveVar:"REMOVE_FILE_ENTRY_AFTER_FINISH"} ; misc GUI variables -; ------------------------- +;------------------------------------------------------------- GUI.HDtemplate := { ddList: "" ; Hard drive template dropdown list . "|Conner CFA170A - 163MB||" . "Rodime R0201 - 5MB|" @@ -187,7 +188,7 @@ GUI.CPUCores := procCountDDList() GUI CHDMAN options Format: -------- +;------------------------------------------------------------- name: String - Friendly name of option - used as a reference paramString: String - String used in actual chdman command description: String - String used to describe option in the GUI @@ -228,6 +229,7 @@ GUI.chdmanOpt.keepIncomplete := {name: "keepIncomplete", description: "Kee ; Create Main GUI and its elements +;------------------------------------------------------------- createMainGUI() createProgressBars() createMenus() @@ -246,18 +248,15 @@ mainAppHWND := winExist(APP_MAIN_NAME) log(APP_MAIN_NAME " ready.") return -; ----------------------------------------------------- +;------------------------------------------------------------------------------------------------------------------------- -; ----------------------------------------------------- -; Functions -; ----------------------------------------------------- ; A Menu item was selected -;------------------------- +;------------------------------------------------------------- menuSelected() { global @@ -316,7 +315,7 @@ menuSelected() ; Job selection -; ------------- +;------------------------------------------------------------- selectJob() { global @@ -360,8 +359,9 @@ selectJob() guiCtrl("choose", {dropdownMedia:"|1"}) ; Choose first item in media dropdown and fire the selection } + ; Media selection -; --------------- +;------------------------------------------------------------- selectMedia() { global @@ -465,7 +465,7 @@ selectMedia() ; Refreshes GUI to reflect current settings -; ------------------------------------------------------------------------------- +;------------------------------------------------------------- refreshGUI() { global @@ -538,7 +538,7 @@ refreshGUI() ; User pressed input or output files button ; Show Ext menu -; -------------------------------------------- +;------------------------------------------------------------- buttonExtSelect() { switch a_guicontrol { @@ -551,7 +551,7 @@ buttonExtSelect() ; User selected an extension from the input/output extension menu -; ------------------------------------------------------------------ +;------------------------------------------------------------- menuExtHandler(init:=false) { global job @@ -605,7 +605,7 @@ menuExtHandler(init:=false) ; Scan files and add to queue -; ---------------------------------- +;------------------------------------------------------------- addFolderFiles() { global job, APP_MAIN_NAME @@ -683,7 +683,7 @@ addFolderFiles() ; Listview containting input files was clicked -; -------------------------------------------- +;------------------------------------------------------------- listViewInputFiles() { global @@ -930,7 +930,6 @@ buttonStartJobs() job.msgData := [] job.allReport := "" job.halted := false - job.paused := false job.started := false job.workTally := {started:0, total:job.workQueue.length(), success:0, cancelled:0, skipped:0, withError:0, finished:0, haltedMsg:""} ; Set variables job.workQueueSize := (job.workTally.total < JOB_QUEUE_SIZE)? job.workTally.total : JOB_QUEUE_SIZE ; If number of jobs is less then queue count, only display those progress bars @@ -959,38 +958,69 @@ buttonStartJobs() onMessage(0x004A, "receiveData") ; Receive messages from threads log(job.workTally.total " " stringUpper(job.Cmd) " jobs starting ...") SB_SetText(job.workTally.total " " stringUpper(job.Cmd) " jobs started" , 1) - setTimer, timeoutTimer, 1000 ; Check for timeout of chdman or thread + job.started := true job.startTime := a_TickCount - loop { - if ( job.paused == true ) ; Wait while pause flag is on + setTimer, jobStatusCheck, 500 +} + + +jobStatusCheck() +{ + global job, SHOW_JOB_CONSOLE, TIMEOUT_SEC + + if ( job.workTally.finished == job.workTally.total || job.started == false ) { ; Job queue has finished + finishJobs() + return + } + + if ( job.availPSlots.length() > 0 && job.workQueue.count() > 0 ) { + + thisJob := job.workQueue.removeAt(1) ; Grab the first job from the work queue and assign parameters to variable + + thisJob.pSlot := job.availPSlots.removeAt(1) ; Assign the progress bar a y position from available queue + job.msgData[thisJob.pSlot] := {} + job.msgData[a_index].timeout := 0 + + runCmd := a_ScriptName " threadMode " (SHOW_JOB_CONSOLE == "yes" ? "console" : "") ; "threadmode" flag tells script to run this script as a thread + run % runCmd ,,, pid ; Run it + thisJob.pid := pid + loop { + sleep 150 + sendAppMessage(JSON.Dump(thisJob), "ahk_class AutoHotkey ahk_pid " pid) + if ( thisJob.pid == job.msgData[thisJob.pSlot].pid ) ; Wait for confirmation that msg was receieved + break + } + } + + ; Check for a timeout + loop % job.workQueueSize { ; Loop though jobs + job.msgData[a_index].timeout += 2 ; And to job timeout counter -- job.msgData[a_index].timeout is automatically zeroed out in receiveData() with each data receieve + + if ( job.msgData[a_index].status == "finished" ) continue - if ( job.workTally.finished == job.workTally.total || job.started == false ) ; Job queue has finished - break - else if ( job.availPSlots.length() > 0 && job.workQueue.length() > 0 ) { ; Wait for an available slot in the queue to be added - thisJob := job.workQueue.removeAt(1) ; Grab the first job from the work queue and assign parameters to variable + if ( job.msgData[a_index].timeout >= TIMEOUT_SEC ) { ; If timer counter exceeds threshold, we will assume thread is locked up or has errored out - thisJob.pSlot := job.availPSlots.removeAt(1) ; Assign the progress bar a y position from available queue - job.msgData[thisJob.pSlot] := {} - job.msgData[a_index].timeout := 0 - - runCmd := a_ScriptName " threadMode " (SHOW_JOB_CONSOLE == "yes" ? "console" : "") ; "threadmode" flag tells script to run this script as a thread - run % runCmd ,,, pid ; Run it - thisJob.pid := pid - loop { - sendAppMessage(toJSON(thisJob), "ahk_class AutoHotkey ahk_pid " pid) - sleep 50 - if ( thisJob.pid == job.msgData[thisJob.pSlot].pid ) ; Wait for confirmation that msg was receieved - break - } - sleep 100 + job.msgData[a_index].status := "error" ; Update job.msgData[] with messages and send "error" flag for that job, then parse the data + job.msgData[a_index].log := "Error: Job timed out" + job.msgData[a_index].report := "`nError: Job timed out`n`n`n" + job.msgData[a_index].progress := 100 + job.msgData[a_index].progressText := "Timed out - " job.msgData[a_index].workingTitle + parseData(job.msgData[a_index]) + + cancelJob(job.msgData[a_index].pSlot) ; So attempt to close the process associated with it -- it will Assign a "finished" flag } - sleep 10 } +} + + +finishJobs() +{ + global job, APP_MAIN_NAME, PLAY_SONG_FINISHED - setTimer, timeoutTimer, off + setTimer, jobStatusCheck, off job.started := false job.endTime := a_Tickcount guiToggle("hide", "buttonCancelAllJobs") @@ -1036,7 +1066,9 @@ buttonStartJobs() } -; All jobs have finished or user pressed window close + + +; User closed the finish window ; --------------------------------------------------- 5guiClose() { @@ -1067,7 +1099,7 @@ progressCancelButton() ; --------------------------------- cancelAllJobs() { - global job, JOB_QUEUE_SIZE_LIMIT + global job if ( job.started == false ) return false @@ -1078,11 +1110,12 @@ cancelAllJobs() return false workQueue := job.workQueue ; Create alias of workQueue to work with so the job-check queue loop wont trigger 'finished all jobs' too early - job.paused := true job.workQueue := [] ; Clear the real work Queue now - loop % JOB_QUEUE_SIZE_LIMIT ; To be sure all jobs are cancelled - invalid jobs will be ignored + loop % job.workQueueSize { cancelJob(a_index) + log(" -- QUIT JOB " a_index " ---") + } while ( workQueue.length() > 0 ) { thisJob := workQueue.removeAt(1) @@ -1094,7 +1127,6 @@ cancelAllJobs() guiCtrl({progressAll:percentAll, progressTextAll:job.workTally.finished " jobs of " job.workTally.total " completed " (job.workTally.withError ? "(" job.workTally.withError " error" (job.workTally.withError>1? "s)":")") : "")" - " percentAll "%" }) } job.started := false - job.paused := false return true } @@ -1108,42 +1140,15 @@ cancelJob(pSlot) if ( !job.msgData[pSlot].pid || job.msgData[pSlot].status == "finished" ) return - - job.msgData[pSlot].log := "Attempting to Cancel: " job.msgData[pSlot].workingTitle - job.msgData[pSlot].progress := 0 - job.msgData[pSlot].progressText := "Cancelling - " job.msgData[pSlot].workingTitle - parseData(job.msgData[pSlot]) ; 'Send' data to be parsed - - process, close, % job.msgData[pSlot].pid - process, WaitClose, % job.msgData[pSlot].pid, 5000 - if ( errorlevel ) { - log ("Couldn't cancel " job.msgData[pSlot].workingTitle " - Error closing job") - return false - } - - process, close, % job.msgData[pSlot].chdmanPID - process, WaitClose, % job.msgData[pSlot].chdmanPID, 5000 - if ( errorlevel ) { - log ("Couldn't cancel " job.msgData[pSlot].workingTitle " - Error closing chdman process") - return false - } - if ( !job.msgData[pSlot].keepIncomplete && fileExist(job.msgData[pSlot].toFileFull) ) { ; Delete incomplete output files if asked to keep - delFiles := deleteFilesReturnList(job.msgData[pSlot].toFileFull) - job.msgData[pSlot].log := delFiles ? "Deleted incomplete file(s): " regExReplace(delFiles, " ,$") : "Error deleting incomplete file(s)!" - job.msgData[pSlot].report := job.msgData[pSlot].log "`n" - job.msgData[pSlot].progress := 100 - parseData(job.msgData[pSlot]) ; 'Send' data to be parsed - } + log("Job " job.msgData[pSlot].idx " - Attempting to Cancel " job.msgData[pSlot].workingTitle) - job.workTally.cancelled++ - job.msgData[pSlot].status := "finished" - job.msgData[pSlot].log := "Job " job.msgData[pSlot].idx " cancelled by user" - job.msgData[pSlot].progressText := "Cancelled - " job.msgData[pSlot].workingTitle - job.msgData[pSlot].progress := 100 - job.msgData[pSlot].report := "`nJob cancelled by user`n" - parseData(job.msgData[pSlot]) ; 'Send' data to be parsed - SB_SetText("Job " job.msgData[pSlot].idx " cancelled", 1) + guiCtrl({("progress" recvData.pSlot):0}) + guiCtrl({("progressText" recvData.pSlot):"Cancelling - " job.msgData[pSlot].workingTitle}) + + job.msgData[pSlot].KILLPROCESS := "true" + JSONStr := JSON.Dump(job.msgData[pSlot]) + sendAppMessage(JSONStr, "ahk_class AutoHotkey ahk_pid " job.msgData[pSlot].pid) } @@ -1226,8 +1231,8 @@ showCHDInfo(fullFileName, currNum, totalNum, guiNum:=3) ; ---------------------------------------- receiveData(data1, data2) { - JSON := strGet(numGet(data2 + 2*A_PtrSize) ,, "utf-8") - data := fromJSON(JSON) + JSONStr := strGet(numGet(data2 + 2*A_PtrSize) ,, "utf-8") + data := JSON.Load(JSONStr) parseData(data) } @@ -1280,50 +1285,27 @@ parseData(recvData) } } + case "cancelled": + job.workTally.cancelled++ + SB_SetText("Job " job.msgData[pSlot].idx " cancelled", 1) + case "finished": job.allReport .= report[recvData.idx] job.workTally.finished++ percentAll := ceil((job.workTally.finished/job.workTally.total)*100) guiCtrl({progressAll:percentAll, progressTextAll:job.workTally.finished " jobs of " job.workTally.total " completed " (job.workTally.withError ? "(" job.workTally.withError " error" (job.workTally.withError>1? "s)":")") : "")" - " percentAll "%" }) + job.availPSlots.push(recvData.pSlot) ; Add an available slot to progress bar array } if ( recvData.progress <> "" ) guiControl,1:, % "progress" recvData.pSlot, % recvData.progress - if ( recvData.progressText ) { + if ( recvData.progressText ) guiControl,1:, % "progressText" recvData.pSlot, % recvData.progressText - ;log("PSLOT[" recvData.pSlot "] -> " recvData.progressText) - } } -; Job timeout timer -; Timer is set to call this function every 1000 ms -; ---------------------- -timeoutTimer() -{ - global job, TIMEOUT_SEC - - loop % job.workQueueSize { ; Loop though jobs - job.msgData[a_index].timeout += 1 ; And add 1 seconds to job timeout counter -- job.msgData[a_index].timeout is automatically zeroed out in receiveData() with each data receieve - - if ( job.msgData[a_index].status == "finished" ) - continue - - if ( job.msgData[a_index].timeout >= TIMEOUT_SEC ) { ; If timer counter exceeds threshold, we will assume thread is locked up or has errored out - - job.msgData[a_index].status := "error" ; Update job.msgData[] with messages and send "error" flag for that job, then parse the data - job.msgData[a_index].log := "Error: Job timed out" - job.msgData[a_index].report := "`nError: Job timed out`n`n`n" - job.msgData[a_index].progress := 100 - job.msgData[a_index].progressText := "Timed out - " job.msgData[a_index].workingTitle - parseData(job.msgData[a_index]) - sleep 100 - cancelJob(job.msgData[a_index].pSlot) ; So attempt to close the process associated with it -- it will Assign a "finished" flag - } - } -} @@ -1686,17 +1668,17 @@ playSound() ; Send data across script instances ; ------------------------------------------------------- -sendAppMessage(ByRef StringToSend, ByRef TargetScriptTitle) +sendAppMessage(stringToSend, targetScriptTitle) { VarSetCapacity(CopyDataStruct, 3*A_PtrSize, 0) - SizeInBytes := strPutVar(StringToSend, StringToSend, "utf-8") + SizeInBytes := strPutVar(stringToSend, stringToSend, "utf-8") NumPut(SizeInBytes, CopyDataStruct, A_PtrSize) - NumPut(&StringToSend, CopyDataStruct, 2*A_PtrSize) + NumPut(&stringToSend, CopyDataStruct, 2*A_PtrSize) Prev_DetectHiddenWindows := A_DetectHiddenWindows Prev_TitleMatchMode := A_TitleMatchMode DetectHiddenWindows On SetTitleMatchMode 2 - SendMessage, 0x4a, 0, &CopyDataStruct,, % TargetScriptTitle + SendMessage, 0x4a, 0, &CopyDataStruct,, % targetScriptTitle DetectHiddenWindows %Prev_DetectHiddenWindows% SetTitleMatchMode %Prev_TitleMatchMode% return errorLevel @@ -1704,7 +1686,7 @@ sendAppMessage(ByRef StringToSend, ByRef TargetScriptTitle) strPutVar(string, ByRef var, encoding) { - varSetCapacity( var, StrPut(string, encoding) * ((encoding="utf-16"||encoding="cp1200") ? 2 : 1) ) + varSetCapacity( var, StrPut(string, encoding) * ((encoding="utf-8"||encoding="cp1200") ? 2 : 1) ) return StrPut(string, &var, encoding) } @@ -2306,8 +2288,8 @@ checkForUpdates(arg1:="", userClick:=false) obj.assets[3].browser_download_url = URL *should* point to namDHC_vx.xx.zip obj.created_at = date created */ - JSON := URLDownloadToVar(GITHUB_REPO_URL) - obj := json2(JSON) + JSONStr := URLDownloadToVar(GITHUB_REPO_URL) + obj := JSON.Load(JSONStr) if ( !isObject(obj) ) { log("Error updating: Update info invalid") @@ -2395,7 +2377,7 @@ killAllProcess() loop { process, close, % "chdman.exe" - if ( !errorLevel ) + if ( errorLevel == 0 ) break } diff --git a/threads.ahk b/threads.ahk index b6dd97b..e53b85e 100644 --- a/threads.ahk +++ b/threads.ahk @@ -1,25 +1,24 @@ onMessage(0x4a, "thread_receiveData") -onExit("thread_finishJob", -1) gui 1:show, hide, % APP_RUN_JOB_NAME menu tray, noIcon thread_log("Started ... ") -recvData := {}, sendData := {}, mainAppHWND := winExist(APP_MAIN_NAME) +thread_recvData := {}, thread_sendData := {}, mainAppHWND := winExist(APP_MAIN_NAME) ; Set up console window ; -------------------------------------------------------------------------------------------- console := new Console() console.setConsoleTitle(APP_RUN_CONSOLE_NAME) ;winSet, Style, ^0x80000 , % "ahk_id " console.getConsoleHWND() ; Remove close button on window -if ( a_args[2] <> "console" ) ; Hide console +if ( SHOW_JOB_CONSOLE == "no" ) ; Hide console winHide , % "ahk_id " console.getConsoleHWND() ; Handshakiing ; -------------------------------------------------------------------------------------------- thread_log("Handshaking with " APP_MAIN_NAME "... ") -while ( !recvData.cmd && !recvData.idx ) { +while ( !thread_recvData.cmd && !thread_recvData.idx ) { thread_log(".") sleep 10 if ( a_index > 1000 ) { @@ -28,35 +27,36 @@ while ( !recvData.cmd && !recvData.idx ) { } } thread_log("OK!`n" - . "Starting a " stringUpper(recvData.cmd) " job`n`n" - . "Working job file: " recvData.workingTitle "`n" - . "Working directory: " recvData.workingDir "`n") + . "Starting a " stringUpper(thread_recvData.cmd) " job`n`n" + . "Working job file: " thread_recvData.workingTitle "`n" + . "Working directory: " thread_recvData.workingDir "`n") -mergeObj(recvData, sendData) ; Assign recvData to sendData as we will be sending the same info back and forth +mergeObj(thread_recvData, thread_sendData) ; Assign thread_recvData to thread_sendData as we will be sending the same info back and forth -sendData.pid := dllCall("GetCurrentProcessId") -sendData.progress := 0 -sendData.log := "Preparing " stringUpper(recvData.cmd) " - " recvData.workingTitle -sendData.progressText := "Preparing - " recvData.workingTitle +thread_sendData.pid := dllCall("GetCurrentProcessId") +thread_sendData.progress := 0 +thread_sendData.log := "Preparing " stringUpper(thread_recvData.cmd) " - " thread_recvData.workingTitle +thread_sendData.progressText := "Preparing - " thread_recvData.workingTitle thread_sendData() ; Create output folder if it dosent exist ; ------------------------------------------------------------------------------------------------------------------- -if ( fileExist(recvData.outputFolder) <> "D" ) { - if ( createFolder(recvData.outputFolder) ) { - thread_log("Created directory " recvData.outputFolder "`n") +if ( fileExist(thread_recvData.outputFolder) <> "D" ) { + if ( createFolder(thread_recvData.outputFolder) ) { + thread_log("Created directory " thread_recvData.outputFolder "`n") } else { sleep 50 - thread_log("Error creating directory " recvData.outputFolder "`n") + thread_log("Error creating directory " thread_recvData.outputFolder "`n") - sendData.status := "error" - sendData.log := "Error creating directory " recvData.outputFolder - sendData.report := "`n" "Error creating directory " recvData.outputFolder "`n" - sendData.progressText := "Error creating directory - " recvData.workingTitle - sendData.progress := 100 + thread_sendData.status := "error" + thread_sendData.log := "Error creating directory " thread_recvData.outputFolder + thread_sendData.report := "`n" "Error creating directory " thread_recvData.outputFolder "`n" + thread_sendData.progressText := "Error creating directory - " thread_recvData.workingTitle + thread_sendData.progress := 100 thread_sendData() + thread_finishJob() exitApp } } @@ -64,96 +64,97 @@ if ( fileExist(recvData.outputFolder) <> "D" ) { ; Zipfile was supplied as source ; ------------------------------------------------------------------------------------------------------------------- -if ( recvData.fromFileExt == "zip" ) { +if ( thread_recvData.fromFileExt == "zip" ) { sleep 50 - sendData.status := "unzipping" - sendData.progress := 0 - sendData.progressText := "Unzipping - " recvData.fromFile + thread_sendData.status := "unzipping" + thread_sendData.progress := 0 + thread_sendData.progressText := "Unzipping - " thread_recvData.fromFile thread_sendData() - tempZipDirectory := DIR_TEMP "\" recvData.fromFileNoExt + tempZipDirectory := DIR_TEMP "\" thread_recvData.fromFileNoExt folderDelete(tempZipDirectory, 3, 25, 1) ; Delete folder and its contents if it exists createFolder(tempZipDirectory) ; Create the folder if ( fileExist(tempZipDirectory) == "D" ) { - thread_log("Unzipping " recvData.fromFileFull "`nUnzipping to: " tempZipDirectory) + thread_log("Unzipping " thread_recvData.fromFileFull "`nUnzipping to: " tempZipDirectory) setTimer, thread_timeout, % (TIMEOUT_SEC*1000)*3 ; Set timeout timer timeout time x2 - if ( fileUnzip := thread_unzip(recvData.fromFileFull, tempZipDirectory) ) { - sendData.status := "unzipping" - sendData.log := "Unzipped " recvData.fromFileFull " successfully" - sendData.report := "Unzipped " recvData.workingTitle " successfully`n" - sendData.progress := 100 - sendData.progressText := "Unzipped successfully - " recvData.workingTitle + if ( fileUnzip := thread_unzip(thread_recvData.fromFileFull, tempZipDirectory) ) { + thread_sendData.log := "Unzipped " thread_recvData.fromFileFull " successfully" + thread_sendData.report := "Unzipped " thread_recvData.workingTitle " successfully`n" + thread_sendData.progress := 100 + thread_sendData.progressText := "Unzipped successfully - " thread_recvData.workingTitle thread_sendData() - thread_log("Unzipped " recvData.fromFileFull " successfully`n") + thread_log("Unzipped " thread_recvData.fromFileFull " successfully`n") - recvData.fromFileFull := fileUnzip.full - recvData.fromFile := fileUnzip.file - recvData.fromFileNoExt := fileUnzip.noExt - recvData.fromFileExt := fileUnzip.ext - mergeObj(recvData, sendData) + thread_recvData.fromFileFull := fileUnzip.full + thread_recvData.fromFile := fileUnzip.file + thread_recvData.fromFileNoExt := fileUnzip.noExt + thread_recvData.fromFileExt := fileUnzip.ext + mergeObj(thread_recvData, thread_sendData) } - else error := ["Error unzipping file '" recvData.fromFileFull "'", "Error unzipping file - " recvData.fromFileFull] + else error := ["Error unzipping file '" thread_recvData.fromFileFull "'", "Error unzipping file - " thread_recvData.fromFileFull] } - else error := ["Error creating temporary directory '" DIR_TEMP "\" recvData.fromFileNoExt "'", "Error creating temp directory"] + else error := ["Error creating temporary directory '" DIR_TEMP "\" thread_recvData.fromFileNoExt "'", "Error creating temp directory"] setTimer, thread_timeout, off if ( error ) { - sendData.status := "error" - sendData.log := error[1] - sendData.report := "`n" error[1] "`n" - sendData.progressText := error[2] " - " recvData.workingTitle - sendData.progress := 100 + thread_sendData.status := "error" + thread_sendData.log := error[1] + thread_sendData.report := "`n" error[1] "`n" + thread_sendData.progressText := error[2] " - " thread_recvData.workingTitle + thread_sendData.progress := 100 thread_sendData() if ( fileExist(tempZipDirectory) ) thread_deleteDir(tempZipDirectory, 1) ; Delete temp directory + thread_finishJob() exitApp } } sleep 50 -fromFile := recvData.fromFileFull ? """" recvData.fromFileFull """" : "" -toFile := recvData.toFileFull ? """" recvData.toFileFull """" : "" -cmdLine := CHDMAN_FILE_LOC . " " . recvData.cmd . recvData.cmdOpts . " -v" . (fromFile ? " -i " fromFile : "") . (toFile ? " -o " toFile : "") +fromFile := thread_recvData.fromFileFull ? """" thread_recvData.fromFileFull """" : "" +toFile := thread_recvData.toFileFull ? """" thread_recvData.toFileFull """" : "" +cmdLine := CHDMAN_FILE_LOC . " " . thread_recvData.cmd . thread_recvData.cmdOpts . " -v" . (fromFile ? " -i " fromFile : "") . (toFile ? " -o " toFile : "") thread_log("`nCommand line: " cmdLine "`n`n") -sendData.progress := 0 -sendData.log := "Starting " stringUpper(recvData.cmd) " - " recvData.workingTitle -sendData.progressText := "Starting job - " recvData.workingTitle +thread_sendData.progress := 0 +thread_sendData.log := "Starting " stringUpper(thread_recvData.cmd) " - " thread_recvData.workingTitle +thread_sendData.progressText := "Starting job - " thread_recvData.workingTitle thread_sendData() setTimer, thread_timeout, % (TIMEOUT_SEC*1000) ; Set timeout timer -output := runCMD(cmdLine, recvData.workingDir, "CP0", "thread_parseCHDMANOutput") +output := runCMD(cmdLine, thread_recvData.workingDir, "CP0", "thread_parseCHDMANOutput") setTimer, thread_timeout, off rtnError := thread_checkForErrors(output.msg) ; CHDMAN was not successfull - Errors were detected ; ------------------------------------------------------------------------------------------------------------------- -if ( (rtnError && !inStr(rtnError,"file already exists")) || (inStr(rtnError, "file already exists") && !inStr(recvData.cmdOpts, "-f")) ) { +if ( (rtnError && !inStr(rtnError,"file already exists")) || (inStr(rtnError, "file already exists") && !inStr(thread_recvData.cmdOpts, "-f")) ) { - if ( !recvData.keepIncomplete && rtnError <> "file already exists" ) { ; Delete incomplete output files, but dont delete "incomplete" output file if the error is that the file exists - delFiles := deleteFilesReturnList(recvData.toFileFull) - sendData.log := delFiles ? "Deleted incomplete file(s): " regExReplace(delFiles, " ,$") : "Error deleting incomplete file(s)!" - sendData.report := sendData.log "`n" - sendData.progress := 100 + if ( !thread_recvData.keepIncomplete && rtnError <> "file already exists" ) { ; Delete incomplete output files, but dont delete "incomplete" output file if the error is that the file exists + delFiles := deleteFilesReturnList(thread_recvData.toFileFull) + thread_sendData.log := delFiles ? "Deleted incomplete file(s): " regExReplace(delFiles, " ,$") : "Error deleting incomplete file(s)!" + thread_sendData.report := thread_sendData.log "`n" + thread_sendData.progress := 100 thread_sendData() - thread_log(sendData.log "`n") + thread_log(thread_sendData.log "`n") } - sendData.status := "error" - sendData.log := rtnError - sendData.report := "`n" (inStr(sendData.log, "Error") ? "" : "Error: ") sendData.log "`n" - sendData.progressText := regExReplace(sendData.log, "`n|`r", "") " - " recvData.workingTitle - sendData.progress := 100 + thread_sendData.status := "error" + thread_sendData.log := rtnError + thread_sendData.report := "`n" (inStr(thread_sendData.log, "Error") ? "" : "Error: ") thread_sendData.log "`n" + thread_sendData.progressText := regExReplace(thread_sendData.log, "`n|`r", "") " - " thread_recvData.workingTitle + thread_sendData.progress := 100 thread_sendData() + thread_finishJob() exitApp } @@ -165,34 +166,35 @@ if ( (rtnError && !inStr(rtnError,"file already exists")) || (inStr(rtnError, "f if ( fileExist(tempZipDirectory) ) thread_deleteDir(tempZipDirectory, 1) ; Always delete temp zip directory and all of its contents -if ( recvData.deleteInputFiles && fileExist(recvData.fromFileFull) ) ; Delete input files if requested - thread_deleteFiles(recvData.fromFileFull) +if ( thread_recvData.deleteInputFiles && fileExist(thread_recvData.fromFileFull) ) ; Delete input files if requested + thread_deleteFiles(thread_recvData.fromFileFull) -if ( recvData.deleteInputDir && fileExist(recvData.workingDir) == "D" ) ; Delete input folder only if its not empty - thread_deleteDir(recvData.workingDir) +if ( thread_recvData.deleteInputDir && fileExist(thread_recvData.workingDir) == "D" ) ; Delete input folder only if its not empty + thread_deleteDir(thread_recvData.workingDir) -if ( inStr(recvData.cmd, "verify") ) +if ( inStr(thread_recvData.cmd, "verify") ) suffx := "verified" -else if ( inStr(recvData.cmd, "extract") ) +else if ( inStr(thread_recvData.cmd, "extract") ) suffx := "extracted media" -else if ( inStr(recvData.cmd, "create") ) +else if ( inStr(thread_recvData.cmd, "create") ) suffx := "created" -else if ( inStr(recvData.cmd, "addmeta") ) +else if ( inStr(thread_recvData.cmd, "addmeta") ) suffx := "added metadata" -else if ( inStr(recvData.cmd, "delmeta") ) +else if ( inStr(thread_recvData.cmd, "delmeta") ) suffx := "deleted metadata" -else if ( inStr(recvData.cmd, "copy") ) +else if ( inStr(thread_recvData.cmd, "copy") ) suffx := "copied metadata" -else if ( inStr(recvData.cmd, "dumpmeta") ) +else if ( inStr(thread_recvData.cmd, "dumpmeta") ) suffx := "dumped metadata" -sendData.status := "success" -sendData.log := "Successfully " suffx " - " recvData.workingTitle -sendData.report := "`nSuccessfully " suffx "`n" -sendData.progressText := "Successfully " suffx " - " recvData.workingTitle -sendData.progress := 100 +thread_sendData.status := "success" +thread_sendData.log := "Successfully " suffx " - " thread_recvData.workingTitle +thread_sendData.report := "`nSuccessfully " suffx "`n" +thread_sendData.progressText := "Successfully " suffx " - " thread_recvData.workingTitle +thread_sendData.progress := 100 thread_sendData() +thread_finishJob() exitApp @@ -238,34 +240,34 @@ thread_checkForErrors(msg) ;------------------------------------------------------------------------- thread_parseCHDMANOutput(data, lineNum, cPID) { - global sendData, recvData, CHDMAN_VERSION_ARRAY, APP_MAIN_NAME, TIMEOUT_SEC - sendData.chdmanPID := cPID ? cPID : "" + global thread_sendData, thread_recvData, CHDMAN_VERSION_ARRAY, APP_MAIN_NAME, TIMEOUT_SEC + thread_sendData.chdmanPID := cPID ? cPID : "" setTimer, thread_timeout, % (TIMEOUT_SEC*1000) ; Reset timeout timer if ( lineNum > 1 ) { if ( stPos := inStr(data, "Compressing") ) { - sendData.status := "compressing" + thread_sendData.status := "compressing" ,stPos += 13, enPos := inStr(data, "%", false, stPos) ; chdman output: "Compressing, 16.8% complete... (ratio=40.5%)" ,stPos2 := inStr(data, "(ratio="), enPos2 := inStr(data, "%)",false,0)+2 ,ratio := subStr(data, stPos2, (enPos2-stPos2)) - ,sendData.progress := subStr(data, stPos, (enPos-stPos)) - ,sendData.progressText := "Compressing - " ((strLen(sendData.toFile) >= 80)? subStr(sendData.toFile, 1, 66) " ..." : sendData.toFile) (sendData.progress>0 ? " - " sendData.progress "% " : "") " " ratio + ,thread_sendData.progress := subStr(data, stPos, (enPos-stPos)) + ,thread_sendData.progressText := "Compressing - " ((strLen(thread_sendData.toFile) >= 80)? subStr(thread_sendData.toFile, 1, 66) " ..." : thread_sendData.toFile) (thread_sendData.progress>0 ? " - " thread_sendData.progress "% " : "") " " ratio } else if ( stPos := inStr(data, "Extracting") ) { - sendData.status := "extracting" + thread_sendData.status := "extracting" ,stPos += 12, enPos := inStr(data, "%", false, stPos) ; chdman output: "Extracting, 39.8% complete..." - ,sendData.progress := subStr(data, stPos, (enPos-stPos)) - ,sendData.progressText := "Extracting - " ((strLen(sendData.toFile) >= 90)? subStr(sendData.toFile, 1, 77) " ..." : sendData.toFile) (sendData.progress>0 ? " - " sendData.progress "%" : "") + ,thread_sendData.progress := subStr(data, stPos, (enPos-stPos)) + ,thread_sendData.progressText := "Extracting - " ((strLen(thread_sendData.toFile) >= 90)? subStr(thread_sendData.toFile, 1, 77) " ..." : thread_sendData.toFile) (thread_sendData.progress>0 ? " - " thread_sendData.progress "%" : "") } else if ( stPos := inStr(data, "Verifying") ) { - sendData.status := "verifying" + thread_sendData.status := "verifying" ,stPos += 11, enPos := inStr(data, "%", false, stPos) ; chdman output: "Verifying, 39.8% complete..." - ,sendData.progress := subStr(data, stPos, (enPos-stPos)) - ,sendData.progressText := "Verifying - " ((strLen(sendData.fromFile) >= 90)? subStr(sendData.fromFile, 1, 78) " ..." : sendData.fromFile) (sendData.progress>0 ? " - " sendData.progress "%" : "") + ,thread_sendData.progress := subStr(data, stPos, (enPos-stPos)) + ,thread_sendData.progressText := "Verifying - " ((strLen(thread_sendData.fromFile) >= 90)? subStr(thread_sendData.fromFile, 1, 78) " ..." : thread_sendData.fromFile) (thread_sendData.progress>0 ? " - " thread_sendData.progress "%" : "") } else if ( !inStr(data,"% ") ) { ; Dont capture text that is in the middle of work - sendData.report := regExReplace(data, "`r|`n", "") "`n" + thread_sendData.report := regExReplace(data, "`r|`n", "") "`n" } } else { ; Wrong chdman version detected @@ -273,14 +275,15 @@ thread_parseCHDMANOutput(data, lineNum, cPID) chdmanVer := trim(subStr(data, 53, (enPos-53))) if ( !inArray(chdmanVer, CHDMAN_VERSION_ARRAY) ) { - sendData.status := "error" - sendData.log := "Error: Wrong CHDMAN version " chdmanVer "`n - Supported versions of CHDMAN are: " arrayToString(CHDMAN_VERSION_ARRAY) - sendData.report := "Wrong CHDMAN version supplied [" chdmanVer "]`nSupported versions of CHDMAN are: " arrayToString(CHDMAN_VERSION_ARRAY) "`n`nJob cancelled.`n" - sendData.progressText := "Error - Wrong CHDMAN version - " recvData.workingTitle - sendData.progress := 100 - thread_log(sendData.log "`n") + thread_sendData.status := "error" + thread_sendData.log := "Error: Wrong CHDMAN version " chdmanVer "`n - Supported versions of CHDMAN are: " arrayToString(CHDMAN_VERSION_ARRAY) + thread_sendData.report := "Wrong CHDMAN version supplied [" chdmanVer "]`nSupported versions of CHDMAN are: " arrayToString(CHDMAN_VERSION_ARRAY) "`n`nJob cancelled.`n" + thread_sendData.progressText := "Error - Wrong CHDMAN version - " thread_recvData.workingTitle + thread_sendData.progress := 100 + thread_log(thread_sendData.log "`n") thread_sendData() + thread_finishJob() exitApp } } @@ -295,26 +298,27 @@ thread_parseCHDMANOutput(data, lineNum, cPID) Send a message to host --------------------------------------------------------------------------------------- What we send home: - sendData.log - (string) General message as to what we are doing - sendData.status - (string) "started", "done", "error" or "killed" indicating status of job - sendData.chdmanPID - (string) PID of this chdman - sendData.report - (string) Output of chdman and other data to be prsented to user at the end of the job - sendData.progress - (integer) progress percentage - sendData.progressText - (integer) progressbar text description + thread_sendData.log - (string) General message as to what we are doing + thread_sendData.status - (string) "started", "done", "error" or "killed" indicating status of job + thread_sendData.chdmanPID - (string) PID of this chdman + thread_sendData.report - (string) Output of chdman and other data to be prsented to user at the end of the job + thread_sendData.progress - (integer) progress percentage + thread_sendData.progressText - (integer) progressbar text description -- and all data from host which was previously sent in object */ thread_sendData(msg:="") { - global recvData, sendData, APP_MAIN_NAME, mainAppHWND + global thread_recvData, thread_sendData, APP_MAIN_NAME, mainAppHWND if ( msg == false ) return - msg := (msg=="") ? sendData : msg - sendAppMessage(toJSON(msg), APP_MAIN_NAME " ahk_id " mainAppHWND) ; Send back the data we've recieved plus any other new info - sendData.log := "" - sendData.report := "" - sendData.status := "" + sleep 50 + msg := (msg=="") ? thread_sendData : msg + sendAppMessage(JSON.Dump(msg), APP_MAIN_NAME " ahk_id " mainAppHWND) ; Send back the data we've recieved plus any other new info + thread_sendData.log := "" + thread_sendData.report := "" + thread_sendData.status := "" sleep 50 } @@ -341,12 +345,56 @@ Recieve messages from host q.hostPID - (string) PID of main program q.id - (number) Unique job id */ -thread_receiveData(wParam, ByRef lParam) +thread_receiveData(wParam, lParam) { - global recvData + global stringAddress := numGet(lParam + 2*A_PtrSize) - recvData := fromJSON(strGet(stringAddress,, "utf-8")) + thread_recvData := JSON.Load(strGet(stringAddress,, "utf-8")) + + if ( thread_recvData.KILLPROCESS == "true" ) { + thread_sendData.log := "Attempting to cancel job " thread_recvData.idx + thread_sendData.progressText := "Cancelling - " thread_recvData.workingTitle + thread_sendData.progress := 0 + thread_sendData() + + thread_log("`nThread cancel signal receieved`n") + + process, close, % thread_recvData.chdmanPID + ;process, WaitClose, % thread_recvData.chdmanPID, 3000 + if ( errorlevel == 0 ) { + thread_sendData.log := "Couldn't cancel " thread_recvData.workingTitle " - Error closing job" + thread_sendData.progressText := "Couldn't cancel - " thread_recvData.workingTitle + thread_sendData.progress := 100 + thread_sendData.report := "`nJob couldn't be cancelled!`n" + thread_sendData() + + thread_log("`n`nJob couldn't be cancelled!`n") + } + else { + thread_sendData.status := "cancelled" + thread_sendData() + + if ( !thread_recvData.keepIncomplete && fileExist(thread_recvData.toFileFull) ) { ; Delete incomplete output files if asked to keep + delFiles := deleteFilesReturnList(thread_recvData.toFileFull) + thread_sendData.log := delFiles ? "Deleted incomplete file(s): " regExReplace(delFiles, " ,$") : "Error deleting incomplete file(s)!" + thread_sendData.report := job.msgData[pSlot].log "`n" + thread_sendData.progress := 100 + thread_sendData() + } + + thread_sendData.log := "Job " thread_recvData.idx " cancelled by user" + thread_sendData.progressText := "Cancelled - " thread_recvData.workingTitle + thread_sendData.progress := 100 + thread_sendData.report := "`nJob cancelled by user`n" + thread_sendData() + + thread_finishJob() + + thread_log("`n`nJob cancelled by user`n") + exitApp + } + } } @@ -354,16 +402,17 @@ thread_receiveData(wParam, ByRef lParam) ; ------------------------------------------------------------------------------------------ thread_timeout() { - global recvData, sendData + global thread_recvData, thread_sendData - sendData.status := "error" - sendData.progressText := "Error - CHDMAN timeout " recvData.workingTitle - sendData.progress := 100 - sendData.log := "Error: Job failed - CHDMAN timed out" - sendData.report := "`nError: Job failed - CHDMAN timed out" "`n" + thread_sendData.status := "error" + thread_sendData.progressText := "Error - CHDMAN timeout " thread_recvData.workingTitle + thread_sendData.progress := 100 + thread_sendData.log := "Error: Job failed - CHDMAN timed out" + thread_sendData.report := "`nError: Job failed - CHDMAN timed out" "`n" thread_sendData() - exitApp ; contains thread_sendData() + thread_finishJob() ; contains thread_sendData() + exitApp } @@ -372,7 +421,7 @@ thread_timeout() ; ----------------------------------------------------------------------------------------- thread_deleteIncompleteFiles(file) { - global recvData, sendData + global thread_recvData, thread_sendData if ( !fileExist(file) ) return false @@ -386,7 +435,7 @@ thread_deleteIncompleteFiles(file) ; ----------------------------------------------------------------------------------------- thread_deleteFiles(delfile) { - global sendData, recvData + global thread_sendData, thread_recvData deleteTheseFiles := getFilesFromCUEGDITOC(delfile) ; Get files to be deleted if ( deleteTheseFiles.length() == 0 ) @@ -401,30 +450,30 @@ thread_deleteFiles(delfile) } if ( log ) - sendData.log := "Deleted Files: " regExReplace(log, ", $") + thread_sendData.log := "Deleted Files: " regExReplace(log, ", $") if ( errLog ) - sendData.log := (log ? log "`n" : "") "Error deleting: " regExReplace(errLog, ", $") ; Remove trailing comma + thread_sendData.log := (log ? log "`n" : "") "Error deleting: " regExReplace(errLog, ", $") ; Remove trailing comma - sendData.report := sendData.log "`n" - sendData.progress := 100 + thread_sendData.report := thread_sendData.log "`n" + thread_sendData.progress := 100 thread_sendData() - thread_log(sendData.log "`n") + thread_log(thread_sendData.log "`n") } thread_deleteDir(dir, delFull:=0) { if ( !delFull && !dllCall("Shlwapi\PathIsDirectoryEmpty", "Str", dir) ) - sendData.log := "Error deleting directory '" dir "' - Not empty" + thread_sendData.log := "Error deleting directory '" dir "' - Not empty" else - sendData.log := folderDelete(dir, 5, 50, delFull) ? "Deleted directory '" dir "'" : "Error deleting directory '" dir "'" + thread_sendData.log := folderDelete(dir, 5, 50, delFull) ? "Deleted directory '" dir "'" : "Error deleting directory '" dir "'" - sendData.report := sendData.log "`n" - sendData.progress := 100 + thread_sendData.report := thread_sendData.log "`n" + thread_sendData.progress := 100 thread_sendData() - thread_log(sendData.log "`n") + thread_log(thread_sendData.log "`n") } @@ -445,7 +494,7 @@ thread_log(newMsg, concat=true) ; ----------------------------------------------------- thread_unzip(file, dir) { - global recvData, sendData + global thread_recvData, thread_sendData try { psh := ComObjCreate("Shell.Application") @@ -458,16 +507,15 @@ thread_unzip(file, dir) loop, Files, % regExReplace(dir, "\\$") "\*.*", FR { zipfile := splitPath(a_LoopFileLongPath) - if ( zipExtInList := inArray(zipfile.ext, recvData.inputFileTypes) ) + if ( zipExtInList := inArray(zipfile.ext, thread_recvData.inputFileTypes) ) return zipfile ; Use only the first file found in the zip temp dir } - return false unzip_showtimer: - sendData.status := "unzipping" - sendData.progress := ceil((psh.Namespace(dir).items().count/zipped)*100) - sendData.progressText := "Unzipping - " recvData.fromFile + thread_sendData.status := "unzipping" + thread_sendData.progress := ceil((psh.Namespace(dir).items().count/zipped)*100) + thread_sendData.progressText := "Unzipping - " thread_recvData.fromFile thread_sendData() return @@ -485,11 +533,13 @@ thread_finishJob() { global - sendData.status := "finished" - sendData.progress := 100 - thread_log(sendData.log? sendData.log "`nFinished!":"") + sleep 50 + thread_sendData.status := "finished" + thread_sendData.progress := 100 + thread_log(thread_sendData.log? thread_sendData.log "`nFinished!":"") thread_sendData() - if ( console.getConsoleHWND() ) + if ( SHOW_JOB_CONSOLE == "yes" ) sleep WAIT_TIME_CONSOLE_SEC*1000 ; Wait x seconds or until user closes window + exitApp } \ No newline at end of file