mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-15 16:16:10 +09:00
token ring stuff wip
This commit is contained in:
@@ -178,7 +178,7 @@ open class GameWorld(
|
|||||||
it[Fluid.NULL] = 0
|
it[Fluid.NULL] = 0
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
val extraFields = HashMap<String, Any?>()
|
val extraFields = HashMap<String, TerrarumSavegameExtrafieldSerialisable?>()
|
||||||
|
|
||||||
// NOTE: genver was here but removed: genver will be written by manually editing the serialising JSON. Reason: the 'genver' string must be found on a fixed offset on the file.
|
// NOTE: genver was here but removed: genver will be written by manually editing the serialising JSON. Reason: the 'genver' string must be found on a fixed offset on the file.
|
||||||
internal var comp = -1 // only gets used when the game saves and loads
|
internal var comp = -1 // only gets used when the game saves and loads
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package net.torvald.terrarum.gameworld
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy interface, except every implementing class must have a no-arg constructor.
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2025-02-27.
|
||||||
|
*/
|
||||||
|
interface TerrarumSavegameExtrafieldSerialisable
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameworld
|
package net.torvald.terrarum.modulebasegame.gameworld
|
||||||
|
|
||||||
import net.torvald.terrarum.gameactors.ActorID
|
import net.torvald.terrarum.gameactors.ActorID
|
||||||
|
import net.torvald.terrarum.gameworld.TerrarumSavegameExtrafieldSerialisable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The whole world is economically isolated system. Economy will be important to make player keep playing,
|
* The whole world is economically isolated system. Economy will be important to make player keep playing,
|
||||||
@@ -11,7 +12,7 @@ import net.torvald.terrarum.gameactors.ActorID
|
|||||||
*
|
*
|
||||||
* Created by minjaesong on 2017-04-23.
|
* Created by minjaesong on 2017-04-23.
|
||||||
*/
|
*/
|
||||||
class GameEconomy {
|
class GameEconomy : TerrarumSavegameExtrafieldSerialisable {
|
||||||
|
|
||||||
val transactionHistory = TransanctionHistory()
|
val transactionHistory = TransanctionHistory()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameworld
|
package net.torvald.terrarum.modulebasegame.gameworld
|
||||||
|
|
||||||
|
import net.torvald.terrarum.gameworld.TerrarumSavegameExtrafieldSerialisable
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
|
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ import kotlin.math.ceil
|
|||||||
*
|
*
|
||||||
* Created by minjaesong on 2024-12-29.
|
* Created by minjaesong on 2024-12-29.
|
||||||
*/
|
*/
|
||||||
class GamePostalService {
|
class GamePostalService : TerrarumSavegameExtrafieldSerialisable {
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package net.torvald.terrarum.modulebasegame.gameworld
|
||||||
|
|
||||||
|
import net.torvald.terrarum.serialise.toBigInt32
|
||||||
|
import net.torvald.terrarum.serialise.toUint
|
||||||
|
import net.torvald.terrarum.serialise.writeBigInt32
|
||||||
|
import java.util.zip.CRC32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Packet data structure
|
||||||
|
*
|
||||||
|
* Endianness: big
|
||||||
|
*
|
||||||
|
* ## The Header
|
||||||
|
*
|
||||||
|
* - (Byte1) Frame Type
|
||||||
|
* - 00 : invalid
|
||||||
|
* - FF : token (an "empty" packet for a Token Ring)
|
||||||
|
* - AA : data
|
||||||
|
* - EE : abort
|
||||||
|
* - 99 : ballot (an initialiser packet for electing the Active Monitor for a Token Ring)
|
||||||
|
* - (Byte1) Frame number. Always 0 for a Token Ring
|
||||||
|
* - (Byte4) Sender MAC address
|
||||||
|
*
|
||||||
|
* ## The Body
|
||||||
|
*
|
||||||
|
* The following specification differs by the Frame Type
|
||||||
|
*
|
||||||
|
* ### Token and Abort
|
||||||
|
*
|
||||||
|
* The Token and Abort frame has no further bytes
|
||||||
|
*
|
||||||
|
* ### Ballot
|
||||||
|
*
|
||||||
|
* - (Byte4) Currently elected Monitor candidate. A NIC examines this number, and if its MAC is larger than
|
||||||
|
* this value, the NIC writes its own MAC to this area and passes the packet to the next NIC; otherwise it
|
||||||
|
* just passes the packet as-is
|
||||||
|
*
|
||||||
|
* ### Data
|
||||||
|
*
|
||||||
|
* - (Byte4) Receiver MAC address
|
||||||
|
* - (Byte4) Length of data in bytes
|
||||||
|
* - (Bytes) The actual data
|
||||||
|
* - (Byte4) CRC-32 of the actual data
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2025-02-27.
|
||||||
|
*/
|
||||||
|
data class IngameNetPacket(val byteArray: ByteArray) {
|
||||||
|
|
||||||
|
fun getFrameType(): String {
|
||||||
|
return when (byteArray.first().toUint()) {
|
||||||
|
0xff -> "token"
|
||||||
|
0xaa -> "data"
|
||||||
|
0xee -> "abort"
|
||||||
|
0x99 -> "ballot"
|
||||||
|
0x00 -> "invalid"
|
||||||
|
else -> "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkIsToken() { if (getFrameType() != "token") throw Error() }
|
||||||
|
private fun checkIsData() { if (getFrameType() != "data") throw Error() }
|
||||||
|
private fun checkIsAbort() { if (getFrameType() != "abort") throw Error() }
|
||||||
|
private fun checkIsBallot() { if (getFrameType() != "ballot") throw Error() }
|
||||||
|
|
||||||
|
fun getBallot(): Int {
|
||||||
|
checkIsBallot()
|
||||||
|
return byteArray.toBigInt32(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBallot(mac: Int) {
|
||||||
|
checkIsBallot()
|
||||||
|
byteArray.writeBigInt32(mac, 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldIintercept(mac: Int) = when (getFrameType()) {
|
||||||
|
"ballot" -> (getBallot() < mac)
|
||||||
|
"data" -> (getDataRecipient() == mac)
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns null if CRC check fails
|
||||||
|
*/
|
||||||
|
fun getDataContents(): ByteArray? {
|
||||||
|
checkIsData()
|
||||||
|
val len = byteArray.toBigInt32(10)
|
||||||
|
val ret = ByteArray(len)
|
||||||
|
System.arraycopy(byteArray, 14, ret, 0, len)
|
||||||
|
val crc0 = byteArray.toBigInt32(14 + len)
|
||||||
|
val crc = CRC32().also { it.update(ret) }.value.toInt()
|
||||||
|
return if (crc != crc0) null else ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDataRecipient(): Int {
|
||||||
|
checkIsData()
|
||||||
|
return byteArray.toBigInt32(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun ByteArray.makeHeader(frameType: Int, mac: Int): ByteArray {
|
||||||
|
this[0] = frameType.toByte()
|
||||||
|
this.writeBigInt32(mac, 2)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeToken(mac: Int) = ByteArray(5).makeHeader(0xff, mac)
|
||||||
|
|
||||||
|
fun makeAbort(mac: Int) = ByteArray(5).makeHeader(0xee, mac)
|
||||||
|
|
||||||
|
fun makeBallot(mac: Int) = ByteArray(9).makeHeader(0x99, mac)
|
||||||
|
|
||||||
|
fun makeData(sender: Int, recipient: Int, data: ByteArray) = ByteArray(18 + data.size).also {
|
||||||
|
it.makeHeader(0xaa, sender)
|
||||||
|
it.writeBigInt32(recipient, 6)
|
||||||
|
it.writeBigInt32(data.size, 10)
|
||||||
|
System.arraycopy(data, 0, it, 14, data.size)
|
||||||
|
val crc = CRC32().also { it.update(data) }.value.toInt()
|
||||||
|
it.writeBigInt32(crc, 14 + data.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package net.torvald.terrarum.modulebasegame.gameworld
|
||||||
|
|
||||||
|
import net.torvald.terrarum.gameworld.TerrarumSavegameExtrafieldSerialisable
|
||||||
|
import java.util.TreeMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages packet-number-to-actual-packet mapping, and safely puts them into the savegame
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2025-02-27.
|
||||||
|
*/
|
||||||
|
class PacketRunner : TerrarumSavegameExtrafieldSerialisable {
|
||||||
|
|
||||||
|
private val ledger = TreeMap<Int, IngameNetPacket>()
|
||||||
|
|
||||||
|
operator fun set(id: Int, packet: IngameNetPacket) {
|
||||||
|
ledger[id] = packet
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(id: Int) = ledger[id]!!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,7 +4,6 @@ import javazoom.jl.decoder.Crc16
|
|||||||
import net.torvald.terrarum.gameitems.ItemID
|
import net.torvald.terrarum.gameitems.ItemID
|
||||||
import net.torvald.terrarum.serialise.toBig64
|
import net.torvald.terrarum.serialise.toBig64
|
||||||
import net.torvald.terrarum.serialise.toUint
|
import net.torvald.terrarum.serialise.toUint
|
||||||
import net.torvald.terrarum.toHex
|
|
||||||
import net.torvald.terrarum.toInt
|
import net.torvald.terrarum.toInt
|
||||||
import net.torvald.terrarum.utils.PasswordBase32
|
import net.torvald.terrarum.utils.PasswordBase32
|
||||||
import net.torvald.unicode.CURRENCY
|
import net.torvald.unicode.CURRENCY
|
||||||
@@ -13,6 +12,15 @@ import java.util.*
|
|||||||
import kotlin.experimental.and
|
import kotlin.experimental.and
|
||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
|
|
||||||
|
class SimplePRNG(seed: Int) {
|
||||||
|
private var state: Int = seed
|
||||||
|
|
||||||
|
fun nextInt(): Int {
|
||||||
|
state = (state * 1664525 + 1013904223) and 0x7FFFFFFF // LCG Algorithm
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2025-02-13.
|
* Created by minjaesong on 2025-02-13.
|
||||||
*/
|
*/
|
||||||
@@ -38,15 +46,98 @@ object RedeemCodeMachine {
|
|||||||
|
|
||||||
val initialPassword = listOf( // will be list of 256 bits of something
|
val initialPassword = listOf( // will be list of 256 bits of something
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||||
"Nam nisl leo, semper a ligula a, sollicitudin congue turpis.",
|
// "Nam nisl leo, semper a ligula a, sollicitudin congue turpis.",
|
||||||
"Aenean id malesuada nibh, vitae accumsan risus.",
|
// "Aenean id malesuada nibh, vitae accumsan risus.",
|
||||||
"Morbi tempus velit et consequat vehicula.",
|
// "Morbi tempus velit et consequat vehicula.",
|
||||||
"Integer varius turpis nec euismod mattis.",
|
// "Integer varius turpis nec euismod mattis.",
|
||||||
"Vivamus dictum non ipsum vitae mollis.",
|
// "Vivamus dictum non ipsum vitae mollis.",
|
||||||
"Quisque tincidunt, diam non dictum sodales, nisl neque aliquet risus, pulvinar posuere lacus est a arcu.",
|
// "Quisque tincidunt, diam non dictum sodales, nisl neque aliquet risus, pulvinar posuere lacus est a arcu.",
|
||||||
"Fusce eu venenatis sapien, non aliquam massa.",
|
// "Fusce eu venenatis sapien, non aliquam massa.",
|
||||||
).map { MessageDigest.getInstance("SHA-256").digest(it.toByteArray()) }
|
).map { MessageDigest.getInstance("SHA-256").digest(it.toByteArray()) }
|
||||||
|
|
||||||
|
private fun shuffleBits(data: ByteArray, seed: Int): ByteArray {
|
||||||
|
return data
|
||||||
|
|
||||||
|
val rng = SimplePRNG(seed)
|
||||||
|
val bitList = mutableListOf<Int>()
|
||||||
|
val unshuffledBits = mutableListOf<Int>()
|
||||||
|
for (i in data.indices) {
|
||||||
|
for (bit in 0..7) {
|
||||||
|
if (i < data.size - 2) {
|
||||||
|
bitList.add((data[i].toInt() shr bit) and 1)
|
||||||
|
} else {
|
||||||
|
unshuffledBits.add((data[i].toInt() shr bit) and 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val indices = bitList.indices.toMutableList()
|
||||||
|
val shuffledBits = MutableList(bitList.size) { 0 }
|
||||||
|
val shuffleMap = indices.toMutableList()
|
||||||
|
val originalToShuffled = indices.toMutableList()
|
||||||
|
|
||||||
|
for (i in indices.indices.reversed()) {
|
||||||
|
val j = rng.nextInt() % (i + 1)
|
||||||
|
shuffledBits[i] = bitList[shuffleMap[j]]
|
||||||
|
originalToShuffled[shuffleMap[j]] = i
|
||||||
|
shuffleMap.removeAt(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
val shuffledBytes = ByteArray(data.size)
|
||||||
|
for (i in shuffledBits.indices) {
|
||||||
|
shuffledBytes[i / 8] = (shuffledBytes[i / 8].toInt() or (shuffledBits[i] shl (i % 8))).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the last two bytes without shuffling
|
||||||
|
for (i in 0 until 16) {
|
||||||
|
shuffledBytes[(data.size - 2) + (i / 8)] = (shuffledBytes[(data.size - 2) + (i / 8)].toInt() or (unshuffledBits[i] shl (i % 8))).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
return shuffledBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unshuffleBits(data: ByteArray, seed: Int): ByteArray {
|
||||||
|
return data
|
||||||
|
|
||||||
|
val rng = SimplePRNG(seed)
|
||||||
|
val bitList = mutableListOf<Int>()
|
||||||
|
val unshuffledBits = mutableListOf<Int>()
|
||||||
|
for (i in data.indices) {
|
||||||
|
for (bit in 0..7) {
|
||||||
|
if (i < data.size - 2) {
|
||||||
|
bitList.add((data[i].toInt() shr bit) and 1)
|
||||||
|
} else {
|
||||||
|
unshuffledBits.add((data[i].toInt() shr bit) and 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val indices = bitList.indices.toMutableList()
|
||||||
|
val shuffleMap = indices.toMutableList()
|
||||||
|
val shuffledToOriginal = MutableList(bitList.size) { 0 }
|
||||||
|
|
||||||
|
for (i in indices.indices.reversed()) {
|
||||||
|
val j = rng.nextInt() % (i + 1)
|
||||||
|
shuffledToOriginal[i] = shuffleMap[j]
|
||||||
|
shuffleMap.removeAt(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
val originalBits = MutableList(bitList.size) { 0 }
|
||||||
|
for (i in bitList.indices) {
|
||||||
|
originalBits[shuffledToOriginal[i]] = bitList[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
val originalBytes = ByteArray(data.size)
|
||||||
|
for (i in originalBits.indices) {
|
||||||
|
originalBytes[i / 8] = (originalBytes[i / 8].toInt() or (originalBits[i] shl (i % 8))).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the last two bytes without unshuffling
|
||||||
|
for (i in 0 until 16) {
|
||||||
|
originalBytes[(data.size - 2) + (i / 8)] = (originalBytes[(data.size - 2) + (i / 8)].toInt() or (unshuffledBits[i] shl (i % 8))).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalBytes
|
||||||
|
}
|
||||||
|
|
||||||
fun encode(itemID: ItemID, amountIndex: Int, isReusable: Boolean, receiver: UUID? = null, msgType: Int = 0, args: String = ""): String {
|
fun encode(itemID: ItemID, amountIndex: Int, isReusable: Boolean, receiver: UUID? = null, msgType: Int = 0, args: String = ""): String {
|
||||||
// filter item ID
|
// filter item ID
|
||||||
val itemType = if (itemID.contains('@')) itemID.substringBefore("@") else ""
|
val itemType = if (itemID.contains('@')) itemID.substringBefore("@") else ""
|
||||||
@@ -64,7 +155,7 @@ object RedeemCodeMachine {
|
|||||||
|
|
||||||
val isShortCode = (unpaddedStr.length <= 60)
|
val isShortCode = (unpaddedStr.length <= 60)
|
||||||
|
|
||||||
val bytes = ByteArray(if (isShortCode) 15 else 30)
|
var bytes = ByteArray(if (isShortCode) 15 else 30)
|
||||||
|
|
||||||
// sync pattern and flags
|
// sync pattern and flags
|
||||||
bytes[0] = (isReusable.toInt() or 0xA4).toByte()
|
bytes[0] = (isReusable.toInt() or 0xA4).toByte()
|
||||||
@@ -102,6 +193,8 @@ object RedeemCodeMachine {
|
|||||||
it.checksum().toInt()
|
it.checksum().toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("Encoding CRC: $crc16")
|
||||||
|
|
||||||
bytes[bytes.size - 2] = crc16.ushr(8).toByte()
|
bytes[bytes.size - 2] = crc16.ushr(8).toByte()
|
||||||
bytes[bytes.size - 1] = crc16.toByte()
|
bytes[bytes.size - 1] = crc16.toByte()
|
||||||
|
|
||||||
@@ -114,7 +207,7 @@ object RedeemCodeMachine {
|
|||||||
basePwd[i] = basePwd[i] xor receiverPwd[i % 16]
|
basePwd[i] = basePwd[i] xor receiverPwd[i % 16]
|
||||||
}
|
}
|
||||||
|
|
||||||
return PasswordBase32.encode(bytes, basePwd)
|
return PasswordBase32.encode(shuffleBits(bytes, crc16), basePwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun UUID.toByteArray(): ByteArray {
|
private fun UUID.toByteArray(): ByteArray {
|
||||||
@@ -144,26 +237,32 @@ object RedeemCodeMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check which one of the 8 keys passes CRC test
|
// check which one of the 8 keys passes CRC test
|
||||||
val crcResults = decodeds.map { decoded ->
|
var key: Int? = null
|
||||||
|
val crcResults = decodeds.map { decoded0 ->
|
||||||
|
val crc0 = decoded0[decoded0.size - 2].toInt().shl(8) or decoded0[decoded0.size - 1].toInt()
|
||||||
|
val decoded = unshuffleBits(decoded0, crc0)
|
||||||
val crc = Crc16().let {
|
val crc = Crc16().let {
|
||||||
for (i in 0 until decoded.size - 2) {
|
for (i in 0 until decoded.size - 2) {
|
||||||
it.add_bits(decoded[i].toInt(), 8)
|
it.add_bits(decoded[i].toInt(), 8)
|
||||||
}
|
}
|
||||||
it.checksum().toInt().and(0xFFFF)
|
it.checksum().toInt()
|
||||||
}
|
}
|
||||||
val crc2 = decoded[decoded.size - 2].toUint().shl(8) or decoded[decoded.size - 1].toUint()
|
|
||||||
|
|
||||||
(crc == crc2)
|
println("Trying CRC $crc0 to $crc")
|
||||||
|
|
||||||
|
((crc == crc0) to decoded).also {
|
||||||
|
if (it.first) key = crc0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if all CRC fails...
|
// if all CRC fails...
|
||||||
if (crcResults.indexOf(true) < 0) {
|
if (key == null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("Decoding CRC: $key")
|
||||||
|
|
||||||
|
val decoded = crcResults.first { it.first }.second
|
||||||
val decoded = decodeds[crcResults.indexOf(true)]
|
|
||||||
|
|
||||||
val reusable = (decoded[0] and 1) != 0.toByte()
|
val reusable = (decoded[0] and 1) != 0.toByte()
|
||||||
|
|
||||||
|
|||||||
@@ -115,3 +115,33 @@ fun ByteArray.toBigInt64(offset: Int = 0): Long {
|
|||||||
}
|
}
|
||||||
fun Byte.toUlong() = java.lang.Byte.toUnsignedLong(this)
|
fun Byte.toUlong() = java.lang.Byte.toUnsignedLong(this)
|
||||||
fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
|
fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
|
||||||
|
|
||||||
|
fun ByteArray.writeBigInt16(value: Int, offset: Int = 0) {
|
||||||
|
for (i in 0..1) {
|
||||||
|
this[i + offset] = value.shr((1 - i) * 8).toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.writeBigInt24(value: Int, offset: Int = 0) {
|
||||||
|
for (i in 0..2) {
|
||||||
|
this[i + offset] = value.shr((2 - i) * 8).toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.writeBigInt32(value: Int, offset: Int = 0) {
|
||||||
|
for (i in 0..3) {
|
||||||
|
this[i + offset] = value.shr((3 - i) * 8).toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.writeBigInt48(value: Long, offset: Int = 0) {
|
||||||
|
for (i in 0..5) {
|
||||||
|
this[i + offset] = value.shr((5 - i) * 8).toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.writeBigInt64(value: Long, offset: Int = 0) {
|
||||||
|
for (i in 0..7) {
|
||||||
|
this[i + offset] = value.shr((7 - i) * 8).toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user