dylbill Posted February 24, 2020 Share Posted February 24, 2020 So I finally got a function working to convert a formID to Hexadecimal after using the .GetformId() function by converting first to binary and then to hex. Here's the relevant code if anyone is interested. Requires SKSE. String[] Property HexDigits Auto String[] Property BinDigits Auto String[] Property EvenNumbers Auto Event OnInit() InitArrays() EndEvent Function InitArrays() BinDigits = New String[16] BinDigits[0] = "0000" BinDigits[1] = "0001" BinDigits[2] = "0010" BinDigits[3] = "0011" BinDigits[4] = "0100" BinDigits[5] = "0101" BinDigits[6] = "0110" BinDigits[7] = "0111" BinDigits[8] = "1000" BinDigits[9] = "1001" BinDigits[10] = "1010" BinDigits[11] = "1011" BinDigits[12] = "1100" BinDigits[13] = "1101" BinDigits[14] = "1110" BinDigits[15] = "1111" HexDigits = New String[16] HexDigits[0] = "0" HexDigits[1] = "1" HexDigits[2] = "2" HexDigits[3] = "3" HexDigits[4] = "4" HexDigits[5] = "5" HexDigits[6] = "6" HexDigits[7] = "7" HexDigits[8] = "8" HexDigits[9] = "9" HexDigits[10] = "a" HexDigits[11] = "b" HexDigits[12] = "c" HexDigits[13] = "d" HexDigits[14] = "e" HexDigits[15] = "f" EvenNumbers = New String[5] EvenNumbers[0] = "0" EvenNumbers[1] = "2" EvenNumbers[2] = "4" EvenNumbers[3] = "6" EvenNumbers[4] = "8" EndFunction String Function ConvertIDToHex(Int IDToConvert) If IDToConvert >= 0 Int A = IDToConvert String IntBinary If EvenNumbers.Find(StringUtil.GetNthChar(A, (StringUtil.GetLength(A as string) - 1))) > -1 IntBinary = ("0") Else IntBinary = ("1") Endif Utility.WaitMenuMode(0.1) While A > 1 A /= 2 If EvenNumbers.Find(StringUtil.GetNthChar(A, (StringUtil.GetLength(A as string) - 1))) > -1 IntBinary = ("0" + IntBinary) Else IntBinary = ("1" + IntBinary) Endif If A <= 1 Float decCheck = ((StringUtil.GetLength(IntBinary) as float) / 4) Float decRemainder = decCheck - Math.Floor(decCheck) If decRemainder == 0.25 IntBinary = "000" + IntBinary Elseif decRemainder == 0.5 IntBinary = "00" + IntBinary Elseif decRemainder == 0.75 IntBinary = "0" + IntBinary Endif ;Debug.MessageBox("Bin is " + IntBinary) Utility.Wait(0.1) Return ConvertBinaryToHex(IntBinary) Endif EndWhile Else Int A = ((IDToConvert - (IDToConvert * 2)) - 1) ;converts negative to positive. String IntBinary If EvenNumbers.Find(StringUtil.GetNthChar(A, (StringUtil.GetLength(A as string) - 1))) > -1 IntBinary = ("1") Else IntBinary = ("0") Endif Utility.WaitMenuMode(0.1) While A > 1 A /= 2 If EvenNumbers.Find(StringUtil.GetNthChar(A, (StringUtil.GetLength(A as string) - 1))) > -1 IntBinary = ("1" + IntBinary) Else IntBinary = ("0" + IntBinary) Endif If A <= 1 Float decCheck = ((StringUtil.GetLength(IntBinary) as float) / 4) Float decRemainder = decCheck - Math.Floor(decCheck) If decRemainder == 0.25 IntBinary = "111" + IntBinary Elseif decRemainder == 0.5 IntBinary = "11" + IntBinary Elseif decRemainder == 0.75 IntBinary = "1" + IntBinary Endif ;Debug.MessageBox("Bin is " + IntBinary) Utility.Wait(0.1) Return "ff" + ConvertBinaryToHex(IntBinary) Endif EndWhile Endif EndFunction String Function ConvertBinaryToHex(String BinToConvert) String HexResult Int I = -4 Int M = (StringUtil.GetLength(BinToConvert) - 1) While I < M I += 4 If I < M String Bin4 = (StringUtil.Substring(BinToConvert, I, 4)) HexResult = (HexResult + HexDigits[BinDigits.Find(Bin4)]) Else Return HexResult Endif EndWhile EndFunction Event SomeEvent() String HexID = ConvertIDToHex(SomeForm.GetformId()) EndEvent My Question is, does anybody know if negative FormID's always start with FF? Because that's what this function assumes. I've tried looking around skyrim for negative ID's and this seems to be the case. Negative IDs occure when using placeAtMe to create new object references, and they all seem to start with FF too. Any help would be appreciated! Link to comment Share on other sites More sharing options...
IsharaMeradin Posted February 25, 2020 Share Posted February 25, 2020 FF IDs reference anything created in-game and stored within the save file. They are not "negative" at least not in my mind. Negative would imply loading before Skyrim.esm at 00 hex and the save file does not. It loads last after all plugins. Link to comment Share on other sites More sharing options...
dylbill Posted February 25, 2020 Author Share Posted February 25, 2020 (edited) I guess I should be more specific. Sometimes when calling the .GetFormId() function, it returns a negative number, and yes I've found this is for created items in game. Example: -16773862 according to Skyrim is FF000D1A in hex. According to a programmer calculator it is FFFF FFFF FF00 0D1A. If I didn't put an FF in front manually for the negative number, my function would return 000D1A. I was wondering if it's always an FF in front for negative ID's, or if there are exceptions. I also remember when I was trying to do this a while ago, the mod follower Recorder returned a negative ID, so I'm just trying to make sure the function is accurate. Edited February 25, 2020 by dylbill Link to comment Share on other sites More sharing options...
ReDragon2013 Posted February 25, 2020 Share Posted February 25, 2020 (edited) Didn't you aware of using modulo? So there is not required any SKSE function. maybe next function is working as you like ;--------------------------------------------------------------- String FUNCTION Dec2Hex(Int m, String sAdd="", Bool bAdd=False) ;Global ;--------------------------------------------------------------- ; convert integer (i10) into hexstring with count of 8 numbers ; sample: 842 -> "0000034A" ; 842 = 8*10² + 4*10 + 2*1 ; base 10 ; 34Ah = 3*16² + 4*16 + A*1 ; base 16 string s int v = 0x10000000 ; init divisor WHILE (v > 0) int j = m / v IF (j < 10) s += j as String ELSEIF (j == 10) s += "A" ELSEIF (j == 11) s += "B" ELSEIF (j == 12) s += "C" ELSEIF (j == 13) s += "D" ELSEIF (j == 14) s += "E" ELSE ;IF (j == 15) s += "F" ;; ELSE ;; s += "?" ENDIF m = m % v ; new remainder as result of modulo IF (v > 1) v = v / 16 ; new divisor ELSE v = 0 ; 1 / 16 = ? safety first ENDIF ENDWHILE IF ( bAdd ) ; appendix RETURN (s + sAdd) ENDIF ;--------- RETURN (sAdd + s) ; prefix by default ENDFUNCTION Edited February 25, 2020 by ReDragon2013 Link to comment Share on other sites More sharing options...
dylbill Posted February 26, 2020 Author Share Posted February 26, 2020 (edited) Hey thanks, I didn't think of using modulo. You still have to account for negative numbers though. I modified your code to do so and it works! String function Dec2Hex(Int m, String sAdd="", Bool bAdd=False) ;Global ;--------------------------------------------------------------- ; convert integer (i10) into hexstring with count of 8 numbers ; sample: 842 -> "0000034A" ; 842 = 8*10² + 4*10 + 2*1 ; base 10 ; 34Ah = 3*16² + 4*16 + A*1 ; base 16 If m >= 0 string s int v = 0x10000000 ; init divisor WHILE (v > 0) int j = m / v IF (j < 10) s += j as String ELSEIF (j == 10) s += "A" ELSEIF (j == 11) s += "B" ELSEIF (j == 12) s += "C" ELSEIF (j == 13) s += "D" ELSEIF (j == 14) s += "E" ELSE ;IF (j == 15) s += "F" ;; ELSE ;; s += "?" ENDIF m = m % v ; new remainder as result of modulo IF (v > 1) v = v / 16 ; new divisor ELSE v = 0 ; 1 / 16 = ? safety first ENDIF ENDWHILE IF ( bAdd ) ; appendix RETURN (s + sAdd) ENDIF ;--------- RETURN (sAdd + s) ; prefix by default Else m += 1 string s int v = 0x10000000 ; init divisor WHILE (v > 0) int k = m / v int j = 15 - (k - (k * 2)) ;converts negative to positive and inverts IF (j < 10) s += j as String ELSEIF (j == 10) s += "A" ELSEIF (j == 11) s += "B" ELSEIF (j == 12) s += "C" ELSEIF (j == 13) s += "D" ELSEIF (j == 14) s += "E" ELSE ;IF (j == 15) s += "F" ;; ELSE ;; s += "?" ENDIF m = m % v ; new remainder as result of modulo IF (v > 1) v = v / 16 ; new divisor ELSE v = 0 ; 1 / 16 = ? safety first ENDIF ENDWHILE IF ( bAdd ) ; appendix RETURN (s + sAdd) ENDIF ;--------- RETURN (sAdd + s) ; prefix by default Endif ENDFUNCTION Thanks a bunch! Edited February 26, 2020 by dylbill Link to comment Share on other sites More sharing options...
dylbill Posted February 26, 2020 Author Share Posted February 26, 2020 One more thing, using the hexDigits array is faster than using the if, andif conditions. So here's the code modified for that: String[] Property HexDigits Auto Event OnInit() InitArrays() EndEvent Function InitArrays() HexDigits = New String[16] HexDigits[0] = "0" HexDigits[1] = "1" HexDigits[2] = "2" HexDigits[3] = "3" HexDigits[4] = "4" HexDigits[5] = "5" HexDigits[6] = "6" HexDigits[7] = "7" HexDigits[8] = "8" HexDigits[9] = "9" HexDigits[10] = "a" HexDigits[11] = "b" HexDigits[12] = "c" HexDigits[13] = "d" HexDigits[14] = "e" HexDigits[15] = "f" EndFunction String function Dec2Hex(Int m, String sAdd="", Bool bAdd=False) ;Global ;--------------------------------------------------------------- ; convert integer (i10) into hexstring with count of 8 numbers ; sample: 842 -> "0000034A" ; 842 = 8*10² + 4*10 + 2*1 ; base 10 ; 34Ah = 3*16² + 4*16 + A*1 ; base 16 If m >= 0 string s int v = 0x10000000 ; init divisor WHILE (v > 0) int j = m / v s += HexDigits[j] m = m % v ; new remainder as result of modulo IF (v > 1) v = v / 16 ; new divisor ELSE v = 0 ; 1 / 16 = ? safety first ENDIF ENDWHILE IF ( bAdd ) ; appendix RETURN (s + sAdd) ENDIF ;--------- RETURN (sAdd + s) ; prefix by default Else m += 1 string s int v = 0x10000000 ; init divisor WHILE (v > 0) int k = m / v int j = 15 - (k - (k * 2)) ;converts negative to positive and inverts s += HexDigits[j] m = m % v ; new remainder as result of modulo IF (v > 1) v = v / 16 ; new divisor ELSE v = 0 ; 1 / 16 = ? safety first ENDIF ENDWHILE IF ( bAdd ) ; appendix RETURN (s + sAdd) ENDIF ;--------- RETURN (sAdd + s) ; prefix by default Endif ENDFUNCTION Thanks again! Link to comment Share on other sites More sharing options...
Deleted3897072User Posted February 26, 2020 Share Posted February 26, 2020 FormIds from esl modules begin FE. You might need to allow for that if you haven't already. Link to comment Share on other sites More sharing options...
dylbill Posted February 26, 2020 Author Share Posted February 26, 2020 Hey thanks, but the function is now working for all formID's. I checked for ESL Id's and is working for them too. Link to comment Share on other sites More sharing options...
dylbill Posted February 26, 2020 Author Share Posted February 26, 2020 (edited) One last tweak. For negative numbers you can just use another array to find the hex digit, instead of turning to positive and inverting: String[] Property HexDigits Auto Int[] Property NegativeDecDigits Auto Event OnInit() InitArrays() EndEvent Function InitArrays() HexDigits = New String[16] HexDigits[0] = "0" HexDigits[1] = "1" HexDigits[2] = "2" HexDigits[3] = "3" HexDigits[4] = "4" HexDigits[5] = "5" HexDigits[6] = "6" HexDigits[7] = "7" HexDigits[8] = "8" HexDigits[9] = "9" HexDigits[10] = "a" HexDigits[11] = "b" HexDigits[12] = "c" HexDigits[13] = "d" HexDigits[14] = "e" HexDigits[15] = "f" NegativeDecDigits = New Int[16] NegativeDecDigits[0] = -15 NegativeDecDigits[1] = -14 NegativeDecDigits[2] = -13 NegativeDecDigits[3] = -12 NegativeDecDigits[4] = -11 NegativeDecDigits[5] = -10 NegativeDecDigits[6] = -9 NegativeDecDigits[7] = -8 NegativeDecDigits[8] = -7 NegativeDecDigits[9] = -6 NegativeDecDigits[10] = -5 NegativeDecDigits[11] = -4 NegativeDecDigits[12] = -3 NegativeDecDigits[13] = -2 NegativeDecDigits[14] = -1 NegativeDecDigits[15] = 0 EndFunction String function ConvertIDToHex(Int m) ;--------------------------------------------------------------- ; convert integer (i10) into hexstring with count of 8 numbers ; sample: 842 -> "0000034A" ; 842 = 8*10² + 4*10 + 2*1 ; base 10 ; 34Ah = 3*16² + 4*16 + A*1 ; base 16 If m >= 0 string s int v = 0x10000000 ; init divisor While (v > 0) int j = m / v s += HexDigits[j] m = m % v ; new remainder as result of modulo If (v > 1) v = v / 16 ; new divisor Else v = 0 ; 1 / 16 = ? safety first Endif Endwhile Return (s) Else m += 1 string s int v = 0x10000000 ; init divisor While (v > 0) int j = m / v s += HexDigits[(NegativeDecDigits.Find(j))] m = m % v ; new remainder as result of modulo If (v > 1) v = v / 16 ; new divisor Else v = 0 ; 1 / 16 = ? safety first Endif Endwhile return (s) Endif EndFunction Edited February 26, 2020 by dylbill Link to comment Share on other sites More sharing options...
ReDragon2013 Posted February 29, 2020 Share Posted February 29, 2020 (edited) I do not believe it's a good idea to use array(s) for this kind of function. I would like to use that as convenience without any initialization.My final version would look as follow with minimum of function variables inside. Just in case the parameters for string and bool may be stripped, if that is desiredthe last code lines for appending a string to the return value should be removed as well. ;----------------------------------------------------------- String FUNCTION Dec2Hex(Int n, String sA="", Bool bA=False) Global ;----------------------------------------------------------- ; sample: 842 -> "0000034A" ; 842 = 8*10² + 4*10 + 2*1 ; base 10 ; 34Ah = 3*16² + 4*16 + A*1 ; base 16 ; convert a signed integer (longint32) into unsigned doubleword as hexstring ; positive: 0x0 .. 0x7fffffff ; negative: 0x80000000 .. 0xffffffff int T ; Zero by default IF (n < 0) n = 0x7FFFFFFF + n + 1 ; transform negative to positive (2147483647 == 0x7FFFFFFF) T = 8 ENDIF T = (n / 0x10000000) + T ; store leading halfbyte string s ; "" by default int j int i = 8 ; minimum length of string (that will be the return value) WHILE (i) ; (i > 0) i = i - 1 IF (i) j = n % 16 ; n iMODULO 16 == (n AND 15) n = n / 16 ; n iDIV 16 == (n SHR 4) ELSE j = T ; get stored halfbyte ENDIF IF (j < 10) s = (j as String) + s ; "0" .. "9" ELSEIF (j > 13) IF (j == 15) s = "f" + s ELSE s = "e" + s ENDIF ELSEIF (j > 11) IF (j == 13) s = "d" + s ELSE s = "c" + s ENDIF ELSE IF (j == 11) s = "b" + s ELSE s = "a" + s ENDIF ENDIF ENDWHILE ; -------------------------------- append parameter string IF ( bA ) s = s + sA ; as suffix ELSE s = sA + s ; as prefix (by default) ENDIF RETURN sENDFUNCTION Edited February 29, 2020 by ReDragon2013 Link to comment Share on other sites More sharing options...
Recommended Posts