mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-15 16:34:51 +01:00
refactored gametime and utils folder which decouples entrance and sign from channel servers
This commit is contained in:
8
utils/bfutil/bfutil.go
Normal file
8
utils/bfutil/bfutil.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package bfutil
|
||||
|
||||
import "bytes"
|
||||
|
||||
// UpToNull returns the given byte slice's data, up to (not including) the first null byte.
|
||||
func UpToNull(data []byte) []byte {
|
||||
return bytes.SplitN(data, []byte{0x00}, 2)[0]
|
||||
}
|
||||
21
utils/byteframe/LICENSE
Normal file
21
utils/byteframe/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Andrew Gutekanst
|
||||
|
||||
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.
|
||||
379
utils/byteframe/byteframe.go
Normal file
379
utils/byteframe/byteframe.go
Normal file
@@ -0,0 +1,379 @@
|
||||
package byteframe
|
||||
|
||||
/*
|
||||
This is HEAVILY based on the code from
|
||||
https://github.com/sinni800/sgemu/blob/master/Core/Packet.go
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
// ByteFrame is a struct for reading and writing raw byte data.
|
||||
type ByteFrame struct {
|
||||
index uint
|
||||
usedSize uint
|
||||
buf []byte
|
||||
byteOrder binary.ByteOrder
|
||||
}
|
||||
|
||||
// NewByteFrame creates a new ByteFrame with valid default values.
|
||||
// byteOrder defaults to big endian.
|
||||
func NewByteFrame() *ByteFrame {
|
||||
b := &ByteFrame{
|
||||
index: 0,
|
||||
usedSize: 0,
|
||||
buf: make([]byte, 4),
|
||||
byteOrder: binary.BigEndian,
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// NewByteFrameFromBytes creates a new ByteFrame with valid default values.
|
||||
// makes a copy of the given buf and initalizes with it.
|
||||
// byteOrder defaults to big endian.
|
||||
func NewByteFrameFromBytes(buf []byte) *ByteFrame {
|
||||
b := &ByteFrame{
|
||||
index: 0,
|
||||
usedSize: uint(len(buf)),
|
||||
buf: make([]byte, len(buf)),
|
||||
byteOrder: binary.BigEndian,
|
||||
}
|
||||
copy(b.buf, buf)
|
||||
return b
|
||||
}
|
||||
|
||||
// grow either doubles the backing buffer size, or grows it by the size specified, whichever is larger.
|
||||
func (b *ByteFrame) grow(size uint) {
|
||||
bytesToAdd := uint(0)
|
||||
if size > uint(len(b.buf)) {
|
||||
bytesToAdd = size
|
||||
} else {
|
||||
bytesToAdd = uint(len(b.buf))
|
||||
}
|
||||
|
||||
newBuf := make([]byte, uint(len(b.buf))+bytesToAdd)
|
||||
copy(newBuf, b.buf)
|
||||
b.buf = newBuf
|
||||
}
|
||||
|
||||
// wcheck checks if we have enough space to write.
|
||||
func (b *ByteFrame) wcheck(size uint) {
|
||||
if b.index+size > uint(len(b.buf)) {
|
||||
b.grow(size)
|
||||
}
|
||||
}
|
||||
|
||||
// wprologue is a helpler function to update state after a write.
|
||||
func (b *ByteFrame) wprologue(size uint) {
|
||||
|
||||
tmp := int(b.index+size) - int(b.usedSize)
|
||||
if tmp > 0 {
|
||||
b.usedSize += uint(tmp)
|
||||
}
|
||||
|
||||
b.index += size
|
||||
}
|
||||
|
||||
// rcheck checks if we have enough data to read.
|
||||
func (b *ByteFrame) rcheck(size uint) bool {
|
||||
if b.index+size > uint(len(b.buf)) || b.index+size > b.usedSize+1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *ByteFrame) rprologue(size uint) {
|
||||
b.index += size
|
||||
}
|
||||
|
||||
func (b *ByteFrame) rerr() {
|
||||
panic("Error while reading!")
|
||||
}
|
||||
|
||||
// Seek (implements the io.Seeker interface)
|
||||
func (b *ByteFrame) Seek(offset int64, whence int) (int64, error) {
|
||||
switch whence {
|
||||
case io.SeekStart:
|
||||
if offset > int64(b.usedSize) {
|
||||
return int64(b.index), errors.New("cannot seek beyond the max index")
|
||||
}
|
||||
b.index = uint(offset)
|
||||
break
|
||||
case io.SeekCurrent:
|
||||
newPos := int64(b.index) + offset
|
||||
if newPos > int64(b.usedSize) {
|
||||
return int64(b.index), errors.New("cannot seek beyond the max index")
|
||||
} else if newPos < 0 {
|
||||
return int64(b.index), errors.New("cannot seek before the buffer start")
|
||||
}
|
||||
b.index = uint(newPos)
|
||||
break
|
||||
case io.SeekEnd:
|
||||
newPos := int64(b.usedSize) + offset
|
||||
if newPos > int64(b.usedSize) {
|
||||
return int64(b.index), errors.New("cannot seek beyond the max index")
|
||||
} else if newPos < 0 {
|
||||
return int64(b.index), errors.New("cannot seek before the buffer start")
|
||||
}
|
||||
b.index = uint(newPos)
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
return int64(b.index), nil
|
||||
}
|
||||
|
||||
// Data returns the data from the buffer start up to the max index.
|
||||
func (b *ByteFrame) Data() []byte {
|
||||
return b.buf[:b.usedSize]
|
||||
}
|
||||
|
||||
// DataFromCurrent returns the data from the current index up to the max index.
|
||||
func (b *ByteFrame) DataFromCurrent() []byte {
|
||||
return b.buf[b.index:b.usedSize]
|
||||
}
|
||||
|
||||
func (b *ByteFrame) Index() uint {
|
||||
return b.index
|
||||
}
|
||||
|
||||
// SetLE sets the byte order to litte endian.
|
||||
func (b *ByteFrame) SetLE() {
|
||||
b.byteOrder = binary.LittleEndian
|
||||
}
|
||||
|
||||
// SetBE sets the byte order to big endian.
|
||||
func (b *ByteFrame) SetBE() {
|
||||
b.byteOrder = binary.BigEndian
|
||||
}
|
||||
|
||||
// WriteUint8 writes a uint8 at the current index.
|
||||
func (b *ByteFrame) WriteUint8(x uint8) {
|
||||
b.wcheck(1)
|
||||
b.buf[b.index] = x
|
||||
b.wprologue(1)
|
||||
}
|
||||
|
||||
// WriteBool writes a bool at the current index
|
||||
// (1 byte. true -> 1, false -> 0)
|
||||
func (b *ByteFrame) WriteBool(x bool) {
|
||||
if x {
|
||||
b.WriteUint8(1)
|
||||
} else {
|
||||
b.WriteUint8(0)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteUint16 writes a uint16 at the current index.
|
||||
func (b *ByteFrame) WriteUint16(x uint16) {
|
||||
b.wcheck(2)
|
||||
b.byteOrder.PutUint16(b.buf[b.index:], x)
|
||||
b.wprologue(2)
|
||||
}
|
||||
|
||||
// WriteUint32 writes a uint32 at the current index.
|
||||
func (b *ByteFrame) WriteUint32(x uint32) {
|
||||
b.wcheck(4)
|
||||
b.byteOrder.PutUint32(b.buf[b.index:], x)
|
||||
b.wprologue(4)
|
||||
}
|
||||
|
||||
// WriteUint64 writes a uint64 at the current index.
|
||||
func (b *ByteFrame) WriteUint64(x uint64) {
|
||||
b.wcheck(8)
|
||||
b.byteOrder.PutUint64(b.buf[b.index:], x)
|
||||
b.wprologue(8)
|
||||
}
|
||||
|
||||
// WriteInt8 writes a int8 at the current index.
|
||||
func (b *ByteFrame) WriteInt8(x int8) {
|
||||
b.wcheck(1)
|
||||
b.buf[b.index] = byte(x)
|
||||
b.wprologue(1)
|
||||
}
|
||||
|
||||
// WriteInt16 writes a int16 at the current index.
|
||||
func (b *ByteFrame) WriteInt16(x int16) {
|
||||
b.wcheck(2)
|
||||
b.byteOrder.PutUint16(b.buf[b.index:], uint16(x))
|
||||
b.wprologue(2)
|
||||
}
|
||||
|
||||
// WriteInt32 writes a int32 at the current index.
|
||||
func (b *ByteFrame) WriteInt32(x int32) {
|
||||
b.wcheck(4)
|
||||
b.byteOrder.PutUint32(b.buf[b.index:], uint32(x))
|
||||
b.wprologue(4)
|
||||
}
|
||||
|
||||
// WriteInt64 writes a int64 at the current index.
|
||||
func (b *ByteFrame) WriteInt64(x int64) {
|
||||
b.wcheck(8)
|
||||
b.byteOrder.PutUint64(b.buf[b.index:], uint64(x))
|
||||
b.wprologue(8)
|
||||
}
|
||||
|
||||
// WriteFloat32 writes a float32 at the current index.
|
||||
func (b *ByteFrame) WriteFloat32(x float32) {
|
||||
b.wcheck(4)
|
||||
tmp := math.Float32bits(x)
|
||||
b.byteOrder.PutUint32(b.buf[b.index:], tmp)
|
||||
b.wprologue(4)
|
||||
}
|
||||
|
||||
// WriteFloat64 writes a float64 at the current index
|
||||
func (b *ByteFrame) WriteFloat64(x float64) {
|
||||
b.wcheck(8)
|
||||
tmp := math.Float64bits(x)
|
||||
b.byteOrder.PutUint64(b.buf[b.index:], tmp)
|
||||
b.wprologue(8)
|
||||
}
|
||||
|
||||
// WriteBytes writes a slice of bytes at the current index.
|
||||
func (b *ByteFrame) WriteBytes(x []byte) {
|
||||
b.wcheck(uint(len(x)))
|
||||
copy(b.buf[b.index:], x)
|
||||
b.wprologue(uint(len(x)))
|
||||
}
|
||||
|
||||
// WriteNullTerminatedBytes write a slice bytes with an additional NULL terminator.
|
||||
func (b *ByteFrame) WriteNullTerminatedBytes(x []byte) {
|
||||
b.WriteBytes(x)
|
||||
b.WriteUint8(0)
|
||||
}
|
||||
|
||||
// ReadUint8 reads a uint8 at the current index.
|
||||
func (b *ByteFrame) ReadUint8() (x uint8) {
|
||||
if !b.rcheck(1) {
|
||||
b.rerr()
|
||||
}
|
||||
x = uint8(b.buf[b.index])
|
||||
b.rprologue(1)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadBool reads a bool at the current index
|
||||
// (1 byte. b > 0 -> true, b == 0 -> false)
|
||||
func (b *ByteFrame) ReadBool() (x bool) {
|
||||
tmp := b.ReadUint8()
|
||||
x = tmp > 0
|
||||
return
|
||||
}
|
||||
|
||||
// ReadUint16 reads a uint16 at the current index.
|
||||
func (b *ByteFrame) ReadUint16() (x uint16) {
|
||||
if !b.rcheck(2) {
|
||||
b.rerr()
|
||||
}
|
||||
x = b.byteOrder.Uint16(b.buf[b.index:])
|
||||
b.rprologue(2)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadUint32 reads a uint32 at the current index.
|
||||
func (b *ByteFrame) ReadUint32() (x uint32) {
|
||||
if !b.rcheck(4) {
|
||||
b.rerr()
|
||||
}
|
||||
x = b.byteOrder.Uint32(b.buf[b.index:])
|
||||
b.rprologue(4)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadUint64 reads a uint64 at the current index.
|
||||
func (b *ByteFrame) ReadUint64() (x uint64) {
|
||||
if !b.rcheck(8) {
|
||||
b.rerr()
|
||||
}
|
||||
x = b.byteOrder.Uint64(b.buf[b.index:])
|
||||
b.rprologue(8)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadInt8 reads a int8 at the current index.
|
||||
func (b *ByteFrame) ReadInt8() (x int8) {
|
||||
if !b.rcheck(1) {
|
||||
b.rerr()
|
||||
}
|
||||
x = int8(b.buf[b.index])
|
||||
b.rprologue(1)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadInt16 reads a int16 at the current index.
|
||||
func (b *ByteFrame) ReadInt16() (x int16) {
|
||||
if !b.rcheck(2) {
|
||||
b.rerr()
|
||||
}
|
||||
x = int16(b.byteOrder.Uint16(b.buf[b.index:]))
|
||||
b.rprologue(2)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadInt32 reads a int32 at the current index.
|
||||
func (b *ByteFrame) ReadInt32() (x int32) {
|
||||
if !b.rcheck(4) {
|
||||
b.rerr()
|
||||
}
|
||||
x = int32(b.byteOrder.Uint32(b.buf[b.index:]))
|
||||
b.rprologue(4)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadInt64 reads a int64 at the current index.
|
||||
func (b *ByteFrame) ReadInt64() (x int64) {
|
||||
if !b.rcheck(8) {
|
||||
b.rerr()
|
||||
}
|
||||
x = int64(b.byteOrder.Uint64(b.buf[b.index:]))
|
||||
b.rprologue(8)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadFloat32 reads a float32 at the current index.
|
||||
func (b *ByteFrame) ReadFloat32() (x float32) {
|
||||
if !b.rcheck(4) {
|
||||
b.rerr()
|
||||
}
|
||||
x = math.Float32frombits(b.byteOrder.Uint32(b.buf[b.index:]))
|
||||
b.rprologue(4)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadFloat64 reads a float64 at the current index.
|
||||
func (b *ByteFrame) ReadFloat64() (x float64) {
|
||||
if !b.rcheck(8) {
|
||||
b.rerr()
|
||||
}
|
||||
x = math.Float64frombits(b.byteOrder.Uint64(b.buf[b.index:]))
|
||||
b.rprologue(8)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadBytes reads `size` many bytes at the current index.
|
||||
func (b *ByteFrame) ReadBytes(size uint) (x []byte) {
|
||||
if !b.rcheck(size) {
|
||||
b.rerr()
|
||||
}
|
||||
x = b.buf[b.index : b.index+size]
|
||||
b.rprologue(size)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadNullTerminatedBytes reads bytes up to a NULL terminator.
|
||||
func (b *ByteFrame) ReadNullTerminatedBytes() []byte {
|
||||
tmpData := b.DataFromCurrent()
|
||||
tmp := bytes.SplitN(tmpData, []byte{0x00}, 2)[0]
|
||||
|
||||
if len(tmp) == len(tmpData) {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
b.rprologue(uint(len(tmp)) + 1)
|
||||
return tmp
|
||||
}
|
||||
109
utils/decryption/jpk.go
Normal file
109
utils/decryption/jpk.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package decryption
|
||||
|
||||
/*
|
||||
This code is HEAVILY based from
|
||||
https://github.com/Chakratos/ReFrontier/blob/master/ReFrontier/Unpack.cs
|
||||
*/
|
||||
|
||||
import (
|
||||
"erupe-ce/utils/byteframe"
|
||||
"io"
|
||||
)
|
||||
|
||||
var mShiftIndex = 0
|
||||
var mFlag = byte(0)
|
||||
|
||||
func UnpackSimple(data []byte) []byte {
|
||||
mShiftIndex = 0
|
||||
mFlag = byte(0)
|
||||
|
||||
bf := byteframe.NewByteFrameFromBytes(data)
|
||||
bf.SetLE()
|
||||
header := bf.ReadUint32()
|
||||
|
||||
if header == 0x1A524B4A {
|
||||
bf.Seek(0x2, io.SeekCurrent)
|
||||
jpkType := bf.ReadUint16()
|
||||
|
||||
switch jpkType {
|
||||
case 3:
|
||||
startOffset := bf.ReadInt32()
|
||||
outSize := bf.ReadInt32()
|
||||
outBuffer := make([]byte, outSize)
|
||||
bf.Seek(int64(startOffset), io.SeekStart)
|
||||
ProcessDecode(bf, outBuffer)
|
||||
|
||||
return outBuffer
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func ProcessDecode(data *byteframe.ByteFrame, outBuffer []byte) {
|
||||
outIndex := 0
|
||||
|
||||
for int(data.Index()) < len(data.Data()) && outIndex < len(outBuffer)-1 {
|
||||
if JPKBitShift(data) == 0 {
|
||||
outBuffer[outIndex] = ReadByte(data)
|
||||
outIndex++
|
||||
continue
|
||||
} else {
|
||||
if JPKBitShift(data) == 0 {
|
||||
length := (JPKBitShift(data) << 1) | JPKBitShift(data)
|
||||
off := ReadByte(data)
|
||||
JPKCopy(outBuffer, int(off), int(length)+3, &outIndex)
|
||||
continue
|
||||
} else {
|
||||
hi := ReadByte(data)
|
||||
lo := ReadByte(data)
|
||||
length := int(hi&0xE0) >> 5
|
||||
off := ((int(hi) & 0x1F) << 8) | int(lo)
|
||||
if length != 0 {
|
||||
JPKCopy(outBuffer, off, length+2, &outIndex)
|
||||
continue
|
||||
} else {
|
||||
if JPKBitShift(data) == 0 {
|
||||
length := (JPKBitShift(data) << 3) | (JPKBitShift(data) << 2) | (JPKBitShift(data) << 1) | JPKBitShift(data)
|
||||
JPKCopy(outBuffer, off, int(length)+2+8, &outIndex)
|
||||
continue
|
||||
} else {
|
||||
temp := ReadByte(data)
|
||||
if temp == 0xFF {
|
||||
for i := 0; i < off+0x1B; i++ {
|
||||
outBuffer[outIndex] = ReadByte(data)
|
||||
outIndex++
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
JPKCopy(outBuffer, off, int(temp)+0x1a, &outIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func JPKBitShift(data *byteframe.ByteFrame) byte {
|
||||
mShiftIndex--
|
||||
|
||||
if mShiftIndex < 0 {
|
||||
mShiftIndex = 7
|
||||
mFlag = ReadByte(data)
|
||||
}
|
||||
|
||||
return (byte)((mFlag >> mShiftIndex) & 1)
|
||||
}
|
||||
|
||||
func JPKCopy(outBuffer []byte, offset int, length int, index *int) {
|
||||
for i := 0; i < length; i++ {
|
||||
outBuffer[*index] = outBuffer[*index-offset-1]
|
||||
*index++
|
||||
}
|
||||
}
|
||||
|
||||
func ReadByte(bf *byteframe.ByteFrame) byte {
|
||||
value := bf.ReadUint8()
|
||||
return value
|
||||
}
|
||||
32
utils/gametime/gametime.go
Normal file
32
utils/gametime/gametime.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package gametime
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TimeAdjusted() time.Time {
|
||||
baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60))
|
||||
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), baseTime.Hour(), baseTime.Minute(), baseTime.Second(), baseTime.Nanosecond(), baseTime.Location())
|
||||
}
|
||||
|
||||
func TimeMidnight() time.Time {
|
||||
baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60))
|
||||
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
|
||||
}
|
||||
|
||||
func TimeWeekStart() time.Time {
|
||||
midnight := TimeMidnight()
|
||||
offset := int(midnight.Weekday()) - int(time.Monday)
|
||||
if offset < 0 {
|
||||
offset += 7
|
||||
}
|
||||
return midnight.Add(-time.Duration(offset) * 24 * time.Hour)
|
||||
}
|
||||
|
||||
func TimeWeekNext() time.Time {
|
||||
return TimeWeekStart().Add(time.Hour * 24 * 7)
|
||||
}
|
||||
|
||||
func TimeGameAbsolute() uint32 {
|
||||
return uint32((TimeAdjusted().Unix() - 2160) % 5760)
|
||||
}
|
||||
54
utils/mhfcid/mhfcid.go
Normal file
54
utils/mhfcid/mhfcid.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package mhfcid
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// ConvertCID converts a MHF Character ID String to integer
|
||||
//
|
||||
// Banned characters: 0, I, O, S
|
||||
func ConvertCID(ID string) (r uint32) {
|
||||
if len(ID) != 6 {
|
||||
return
|
||||
}
|
||||
|
||||
m := map[rune]uint32{
|
||||
'1': 0,
|
||||
'2': 1,
|
||||
'3': 2,
|
||||
'4': 3,
|
||||
'5': 4,
|
||||
'6': 5,
|
||||
'7': 6,
|
||||
'8': 7,
|
||||
'9': 8,
|
||||
'A': 9,
|
||||
'B': 10,
|
||||
'C': 11,
|
||||
'D': 12,
|
||||
'E': 13,
|
||||
'F': 14,
|
||||
'G': 15,
|
||||
'H': 16,
|
||||
'J': 17,
|
||||
'K': 18,
|
||||
'L': 19,
|
||||
'M': 20,
|
||||
'N': 21,
|
||||
'P': 22,
|
||||
'Q': 23,
|
||||
'R': 24,
|
||||
'T': 25,
|
||||
'U': 26,
|
||||
'V': 27,
|
||||
'W': 28,
|
||||
'X': 29,
|
||||
'Y': 30,
|
||||
'Z': 31,
|
||||
}
|
||||
|
||||
for i, c := range ID {
|
||||
r += m[c] * uint32(math.Pow(32, float64(i)))
|
||||
}
|
||||
return
|
||||
}
|
||||
112
utils/mhfcourse/mhfcourse.go
Normal file
112
utils/mhfcourse/mhfcourse.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package mhfcourse
|
||||
|
||||
import (
|
||||
_config "erupe-ce/config"
|
||||
"math"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Course struct {
|
||||
ID uint16
|
||||
Expiry time.Time
|
||||
}
|
||||
|
||||
var aliases = map[uint16][]string{
|
||||
1: {"Trial", "TL"},
|
||||
2: {"HunterLife", "HL"},
|
||||
3: {"Extra", "ExtraA", "EX"},
|
||||
4: {"ExtraB"},
|
||||
5: {"Mobile"},
|
||||
6: {"Premium"},
|
||||
7: {"Pallone", "ExtraC"},
|
||||
8: {"Assist", "***ist", "Legend", "Rasta"},
|
||||
9: {"N"},
|
||||
10: {"Hiden", "Secret"},
|
||||
11: {"HunterSupport", "HunterAid", "Support", "Aid", "Royal"},
|
||||
12: {"NBoost", "NetCafeBoost", "Boost"},
|
||||
// 13-19 show up as (unknown)
|
||||
20: {"DEBUG"},
|
||||
21: {"COG_LINK_EXPIRED"},
|
||||
22: {"360_GOLD"},
|
||||
23: {"PS3_TROP"},
|
||||
24: {"COG"},
|
||||
25: {"CAFE_SP"},
|
||||
26: {"NetCafe", "Cafe", "OfficialCafe", "Official"},
|
||||
27: {"HLRenewing", "HLR", "HLRenewal", "HLRenew", "CardHL"},
|
||||
28: {"EXRenewing", "EXR", "EXRenewal", "EXRenew", "CardEX"},
|
||||
29: {"Free"},
|
||||
// 30 = Real NetCafe course
|
||||
}
|
||||
|
||||
func (c Course) Aliases() []string {
|
||||
return aliases[c.ID]
|
||||
}
|
||||
|
||||
func Courses() []Course {
|
||||
courses := make([]Course, 32)
|
||||
for i := range courses {
|
||||
courses[i].ID = uint16(i)
|
||||
}
|
||||
return courses
|
||||
}
|
||||
|
||||
func (c Course) Value() uint32 {
|
||||
return uint32(math.Pow(2, float64(c.ID)))
|
||||
}
|
||||
|
||||
// CourseExists returns true if the named course exists in the given slice
|
||||
func CourseExists(ID uint16, c []Course) bool {
|
||||
for _, course := range c {
|
||||
if course.ID == ID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetCourseStruct returns a slice of Course(s) from a rights integer
|
||||
func GetCourseStruct(rights uint32) ([]Course, uint32) {
|
||||
var resp []Course
|
||||
for _, c := range _config.ErupeConfig.DefaultCourses {
|
||||
resp = append(resp, Course{ID: c})
|
||||
}
|
||||
s := Courses()
|
||||
sort.Slice(s, func(i, j int) bool {
|
||||
return s[i].ID > s[j].ID
|
||||
})
|
||||
var normalCafeCourseSet, netcafeCourseSet, hidenCourseSet bool
|
||||
for _, course := range s {
|
||||
if rights-course.Value() < 0x80000000 {
|
||||
switch course.ID {
|
||||
case 26:
|
||||
if normalCafeCourseSet {
|
||||
break
|
||||
}
|
||||
normalCafeCourseSet = true
|
||||
resp = append(resp, Course{ID: 25})
|
||||
fallthrough
|
||||
case 9:
|
||||
if netcafeCourseSet {
|
||||
break
|
||||
}
|
||||
netcafeCourseSet = true
|
||||
resp = append(resp, Course{ID: 30})
|
||||
case 10:
|
||||
if hidenCourseSet {
|
||||
break
|
||||
}
|
||||
hidenCourseSet = true
|
||||
resp = append(resp, Course{ID: 31})
|
||||
}
|
||||
course.Expiry = time.Date(2030, 1, 1, 0, 0, 0, 0, time.FixedZone("UTC+9", 9*60*60))
|
||||
resp = append(resp, course)
|
||||
rights -= course.Value()
|
||||
}
|
||||
}
|
||||
rights = 0
|
||||
for _, course := range resp {
|
||||
rights += course.Value()
|
||||
}
|
||||
return resp, rights
|
||||
}
|
||||
175
utils/mhfitem/mhfitem.go
Normal file
175
utils/mhfitem/mhfitem.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package mhfitem
|
||||
|
||||
import (
|
||||
_config "erupe-ce/config"
|
||||
"erupe-ce/utils/byteframe"
|
||||
"erupe-ce/utils/token"
|
||||
)
|
||||
|
||||
type MHFItem struct {
|
||||
ItemID uint16
|
||||
}
|
||||
|
||||
type MHFSigilEffect struct {
|
||||
ID uint16
|
||||
Level uint16
|
||||
}
|
||||
|
||||
type MHFSigil struct {
|
||||
Effects []MHFSigilEffect
|
||||
Unk0 uint8
|
||||
Unk1 uint8
|
||||
Unk2 uint8
|
||||
Unk3 uint8
|
||||
}
|
||||
|
||||
type MHFEquipment struct {
|
||||
WarehouseID uint32
|
||||
ItemType uint8
|
||||
Unk0 uint8
|
||||
ItemID uint16
|
||||
Level uint16
|
||||
Decorations []MHFItem
|
||||
Sigils []MHFSigil
|
||||
Unk1 uint16
|
||||
}
|
||||
|
||||
type MHFItemStack struct {
|
||||
WarehouseID uint32
|
||||
Item MHFItem
|
||||
Quantity uint16
|
||||
Unk0 uint32
|
||||
}
|
||||
|
||||
func ReadWarehouseItem(bf *byteframe.ByteFrame) MHFItemStack {
|
||||
var item MHFItemStack
|
||||
item.WarehouseID = bf.ReadUint32()
|
||||
if item.WarehouseID == 0 {
|
||||
item.WarehouseID = token.RNG.Uint32()
|
||||
}
|
||||
item.Item.ItemID = bf.ReadUint16()
|
||||
item.Quantity = bf.ReadUint16()
|
||||
item.Unk0 = bf.ReadUint32()
|
||||
return item
|
||||
}
|
||||
|
||||
func DiffItemStacks(o []MHFItemStack, u []MHFItemStack) []MHFItemStack {
|
||||
// o = old, u = update, f = final
|
||||
var f []MHFItemStack
|
||||
for _, uItem := range u {
|
||||
exists := false
|
||||
for i := range o {
|
||||
if o[i].WarehouseID == uItem.WarehouseID {
|
||||
exists = true
|
||||
o[i].Quantity = uItem.Quantity
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
uItem.WarehouseID = token.RNG.Uint32()
|
||||
f = append(f, uItem)
|
||||
}
|
||||
}
|
||||
for _, oItem := range o {
|
||||
if oItem.Quantity > 0 {
|
||||
f = append(f, oItem)
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (is MHFItemStack) ToBytes() []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(is.WarehouseID)
|
||||
bf.WriteUint16(is.Item.ItemID)
|
||||
bf.WriteUint16(is.Quantity)
|
||||
bf.WriteUint32(is.Unk0)
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func SerializeWarehouseItems(i []MHFItemStack) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(uint16(len(i)))
|
||||
bf.WriteUint16(0) // Unused
|
||||
for _, j := range i {
|
||||
bf.WriteBytes(j.ToBytes())
|
||||
}
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment {
|
||||
var equipment MHFEquipment
|
||||
equipment.Decorations = make([]MHFItem, 3)
|
||||
equipment.Sigils = make([]MHFSigil, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
equipment.Sigils[i].Effects = make([]MHFSigilEffect, 3)
|
||||
}
|
||||
equipment.WarehouseID = bf.ReadUint32()
|
||||
if equipment.WarehouseID == 0 {
|
||||
equipment.WarehouseID = token.RNG.Uint32()
|
||||
}
|
||||
equipment.ItemType = bf.ReadUint8()
|
||||
equipment.Unk0 = bf.ReadUint8()
|
||||
equipment.ItemID = bf.ReadUint16()
|
||||
equipment.Level = bf.ReadUint16()
|
||||
for i := 0; i < 3; i++ {
|
||||
equipment.Decorations[i].ItemID = bf.ReadUint16()
|
||||
}
|
||||
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||
for i := 0; i < 3; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
equipment.Sigils[i].Effects[j].ID = bf.ReadUint16()
|
||||
}
|
||||
for j := 0; j < 3; j++ {
|
||||
equipment.Sigils[i].Effects[j].Level = bf.ReadUint16()
|
||||
}
|
||||
equipment.Sigils[i].Unk0 = bf.ReadUint8()
|
||||
equipment.Sigils[i].Unk1 = bf.ReadUint8()
|
||||
equipment.Sigils[i].Unk2 = bf.ReadUint8()
|
||||
equipment.Sigils[i].Unk3 = bf.ReadUint8()
|
||||
}
|
||||
}
|
||||
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||
equipment.Unk1 = bf.ReadUint16()
|
||||
}
|
||||
return equipment
|
||||
}
|
||||
|
||||
func (e MHFEquipment) ToBytes() []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(e.WarehouseID)
|
||||
bf.WriteUint8(e.ItemType)
|
||||
bf.WriteUint8(e.Unk0)
|
||||
bf.WriteUint16(e.ItemID)
|
||||
bf.WriteUint16(e.Level)
|
||||
for i := 0; i < 3; i++ {
|
||||
bf.WriteUint16(e.Decorations[i].ItemID)
|
||||
}
|
||||
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||
for i := 0; i < 3; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
bf.WriteUint16(e.Sigils[i].Effects[j].ID)
|
||||
}
|
||||
for j := 0; j < 3; j++ {
|
||||
bf.WriteUint16(e.Sigils[i].Effects[j].Level)
|
||||
}
|
||||
bf.WriteUint8(e.Sigils[i].Unk0)
|
||||
bf.WriteUint8(e.Sigils[i].Unk1)
|
||||
bf.WriteUint8(e.Sigils[i].Unk2)
|
||||
bf.WriteUint8(e.Sigils[i].Unk3)
|
||||
}
|
||||
}
|
||||
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||
bf.WriteUint16(e.Unk1)
|
||||
}
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func SerializeWarehouseEquipment(i []MHFEquipment) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(uint16(len(i)))
|
||||
bf.WriteUint16(0) // Unused
|
||||
for _, j := range i {
|
||||
bf.WriteBytes(j.ToBytes())
|
||||
}
|
||||
return bf.Data()
|
||||
}
|
||||
366
utils/mhfmon/mhfmon.go
Normal file
366
utils/mhfmon/mhfmon.go
Normal file
@@ -0,0 +1,366 @@
|
||||
package mhfmon
|
||||
|
||||
const (
|
||||
Mon0 = iota
|
||||
Rathian
|
||||
Fatalis
|
||||
Kelbi
|
||||
Mosswine
|
||||
Bullfango
|
||||
YianKutKu
|
||||
LaoShanLung
|
||||
Cephadrome
|
||||
Felyne
|
||||
VeggieElder
|
||||
Rathalos
|
||||
Aptonoth
|
||||
Genprey
|
||||
Diablos
|
||||
Khezu
|
||||
Velociprey
|
||||
Gravios
|
||||
Mon18
|
||||
Vespoid
|
||||
Gypceros
|
||||
Plesioth
|
||||
Basarios
|
||||
Melynx
|
||||
Hornetaur
|
||||
Apceros
|
||||
Monoblos
|
||||
Velocidrome
|
||||
Gendrome
|
||||
Mon29
|
||||
Ioprey
|
||||
Iodrome
|
||||
Mon32
|
||||
Kirin
|
||||
Cephalos
|
||||
Giaprey
|
||||
CrimsonFatalis
|
||||
PinkRathian
|
||||
BlueYianKutKu
|
||||
PurpleGypceros
|
||||
YianGaruga
|
||||
SilverRathalos
|
||||
GoldRathian
|
||||
BlackDiablos
|
||||
WhiteMonoblos
|
||||
RedKhezu
|
||||
GreenPlesioth
|
||||
BlackGravios
|
||||
DaimyoHermitaur
|
||||
AzureRathalos
|
||||
AshenLaoShanLung
|
||||
Blangonga
|
||||
Congalala
|
||||
Rajang
|
||||
KushalaDaora
|
||||
ShenGaoren
|
||||
GreatThunderbug
|
||||
Shakalaka
|
||||
YamaTsukami
|
||||
Chameleos
|
||||
RustedKushalaDaora
|
||||
Blango
|
||||
Conga
|
||||
Remobra
|
||||
Lunastra
|
||||
Teostra
|
||||
Hermitaur
|
||||
ShogunCeanataur
|
||||
Bulldrome
|
||||
Anteka
|
||||
Popo
|
||||
WhiteFatalis
|
||||
Mon72
|
||||
Ceanataur
|
||||
Hypnocatrice
|
||||
Lavasioth
|
||||
Tigrex
|
||||
Akantor
|
||||
BrightHypnoc
|
||||
RedLavasioth
|
||||
Espinas
|
||||
BurningEspinas
|
||||
WhiteHypnoc
|
||||
AqraVashimu
|
||||
AqraJebia
|
||||
Berukyurosu
|
||||
Mon86
|
||||
Mon87
|
||||
Mon88
|
||||
Pariapuria
|
||||
PearlEspinas
|
||||
KamuOrugaron
|
||||
NonoOrugaron
|
||||
Raviente
|
||||
Dyuragaua
|
||||
Doragyurosu
|
||||
Gurenzeburu
|
||||
Burukku
|
||||
Erupe
|
||||
Rukodiora
|
||||
Unknown
|
||||
Gogomoa
|
||||
Kokomoa
|
||||
TaikunZamuza
|
||||
Abiorugu
|
||||
Kuarusepusu
|
||||
Odibatorasu
|
||||
Disufiroa
|
||||
Rebidiora
|
||||
Anorupatisu
|
||||
Hyujikiki
|
||||
Midogaron
|
||||
Giaorugu
|
||||
MiRu
|
||||
Farunokku
|
||||
Pokaradon
|
||||
Shantien
|
||||
Pokara
|
||||
Mon118
|
||||
Goruganosu
|
||||
Aruganosu
|
||||
Baruragaru
|
||||
Zerureusu
|
||||
Gougarf
|
||||
Uruki
|
||||
Forokururu
|
||||
Meraginasu
|
||||
Diorex
|
||||
GarubaDaora
|
||||
Inagami
|
||||
Varusaburosu
|
||||
Poborubarumu
|
||||
Block1Duremudira
|
||||
Mon133
|
||||
Mon134
|
||||
Mon135
|
||||
Mon136
|
||||
Mon137
|
||||
Mon138
|
||||
Gureadomosu
|
||||
Harudomerugu
|
||||
Toridcless
|
||||
Gasurabazura
|
||||
Kusubami
|
||||
YamaKurai
|
||||
Block2Duremudira
|
||||
Zinogre
|
||||
Deviljho
|
||||
Brachydios
|
||||
BerserkRaviente
|
||||
ToaTesukatora
|
||||
Barioth
|
||||
Uragaan
|
||||
StygianZinogre
|
||||
Guanzorumu
|
||||
SavageDeviljho
|
||||
Mon156
|
||||
Egyurasu
|
||||
Voljang
|
||||
Nargacuga
|
||||
Keoaruboru
|
||||
Zenaserisu
|
||||
GoreMagala
|
||||
BlinkingNargacuga
|
||||
ShagaruMagala
|
||||
Amatsu
|
||||
Eruzerion
|
||||
MusouDuremudira
|
||||
Mon168
|
||||
Seregios
|
||||
Bogabadorumu
|
||||
Mon171
|
||||
MusouBogabadorumu
|
||||
CostumedUruki
|
||||
MusouZerureusu
|
||||
Rappy
|
||||
KingShakalaka
|
||||
)
|
||||
|
||||
type Monster struct {
|
||||
Name string
|
||||
Large bool
|
||||
}
|
||||
|
||||
var Monsters = []Monster{
|
||||
{"Mon0", false},
|
||||
{"Rathian", true},
|
||||
{"Fatalis", true},
|
||||
{"Kelbi", false},
|
||||
{"Mosswine", false},
|
||||
{"Bullfango", false},
|
||||
{"Yian Kut-Ku", true},
|
||||
{"Lao-Shan Lung", true},
|
||||
{"Cephadrome", true},
|
||||
{"Felyne", false},
|
||||
{"Veggie Elder", false},
|
||||
{"Rathalos", true},
|
||||
{"Aptonoth", false},
|
||||
{"Genprey", false},
|
||||
{"Diablos", true},
|
||||
{"Khezu", true},
|
||||
{"Velociprey", false},
|
||||
{"Gravios", true},
|
||||
{"Mon18", false},
|
||||
{"Vespoid", false},
|
||||
{"Gypceros", true},
|
||||
{"Plesioth", true},
|
||||
{"Basarios", true},
|
||||
{"Melynx", false},
|
||||
{"Hornetaur", false},
|
||||
{"Apceros", false},
|
||||
{"Monoblos", true},
|
||||
{"Velocidrome", true},
|
||||
{"Gendrome", true},
|
||||
{"Mon29", false},
|
||||
{"Ioprey", false},
|
||||
{"Iodrome", true},
|
||||
{"Mon32", false},
|
||||
{"Kirin", true},
|
||||
{"Cephalos", false},
|
||||
{"Giaprey", false},
|
||||
{"Crimson Fatalis", true},
|
||||
{"Pink Rathian", true},
|
||||
{"Blue Yian Kut-Ku", true},
|
||||
{"Purple Gypceros", true},
|
||||
{"Yian Garuga", true},
|
||||
{"Silver Rathalos", true},
|
||||
{"Gold Rathian", true},
|
||||
{"Black Diablos", true},
|
||||
{"White Monoblos", true},
|
||||
{"Red Khezu", true},
|
||||
{"Green Plesioth", true},
|
||||
{"Black Gravios", true},
|
||||
{"Daimyo Hermitaur", true},
|
||||
{"Azure Rathalos", true},
|
||||
{"Ashen Lao-Shan Lung", true},
|
||||
{"Blangonga", true},
|
||||
{"Congalala", true},
|
||||
{"Rajang", true},
|
||||
{"Kushala Daora", true},
|
||||
{"Shen Gaoren", true},
|
||||
{"Great Thunderbug", false},
|
||||
{"Shakalaka", false},
|
||||
{"Yama Tsukami", true},
|
||||
{"Chameleos", true},
|
||||
{"Rusted Kushala Daora", true},
|
||||
{"Blango", false},
|
||||
{"Conga", false},
|
||||
{"Remobra", false},
|
||||
{"Lunastra", true},
|
||||
{"Teostra", true},
|
||||
{"Hermitaur", false},
|
||||
{"Shogun Ceanataur", true},
|
||||
{"Bulldrome", true},
|
||||
{"Anteka", false},
|
||||
{"Popo", false},
|
||||
{"White Fatalis", true},
|
||||
{"Mon72", false},
|
||||
{"Ceanataur", false},
|
||||
{"Hypnocatrice", true},
|
||||
{"Lavasioth", true},
|
||||
{"Tigrex", true},
|
||||
{"Akantor", true},
|
||||
{"Bright Hypnocatrice", true},
|
||||
{"Red Lavasioth", true},
|
||||
{"Espinas", true},
|
||||
{"Burning Espinas", true},
|
||||
{"White Hypnocatrice", true},
|
||||
{"Aqra Vashimu", true},
|
||||
{"Aqra Jebia", true},
|
||||
{"Berukyurosu", true},
|
||||
{"Mon86", false},
|
||||
{"Mon87", false},
|
||||
{"Mon88", false},
|
||||
{"Pariapuria", true},
|
||||
{"Pearl Espinas", true},
|
||||
{"Kamu Orugaron", true},
|
||||
{"Nono Orugaron", true},
|
||||
{"Raviente", true}, // + Violent
|
||||
{"Dyuragaua", true},
|
||||
{"Doragyurosu", true},
|
||||
{"Gurenzeburu", true},
|
||||
{"Burukku", false},
|
||||
{"Erupe", false},
|
||||
{"Rukodiora", true},
|
||||
{"Unknown", true},
|
||||
{"Gogomoa", true},
|
||||
{"Kokomoa", false},
|
||||
{"Taikun Zamuza", true},
|
||||
{"Abiorugu", true},
|
||||
{"Kuarusepusu", true},
|
||||
{"Odibatorasu", true},
|
||||
{"Disufiroa", true},
|
||||
{"Rebidiora", true},
|
||||
{"Anorupatisu", true},
|
||||
{"Hyujikiki", true},
|
||||
{"Midogaron", true},
|
||||
{"Giaorugu", true},
|
||||
{"Mi-Ru", true}, // + Musou
|
||||
{"Farunokku", true},
|
||||
{"Pokaradon", true},
|
||||
{"Shantien", true},
|
||||
{"Pokara", false},
|
||||
{"Mon118", false},
|
||||
{"Goruganosu", true},
|
||||
{"Aruganosu", true},
|
||||
{"Baruragaru", true},
|
||||
{"Zerureusu", true},
|
||||
{"Gougarf", true}, // Both
|
||||
{"Uruki", false},
|
||||
{"Forokururu", true},
|
||||
{"Meraginasu", true},
|
||||
{"Diorex", true},
|
||||
{"Garuba Daora", true},
|
||||
{"Inagami", true},
|
||||
{"Varusablos", true},
|
||||
{"Poborubarumu", true},
|
||||
{"1st Block Duremudira", true},
|
||||
{"Mon133", false},
|
||||
{"Mon134", false},
|
||||
{"Mon135", false},
|
||||
{"Mon136", false},
|
||||
{"Mon137", false},
|
||||
{"Mon138", false},
|
||||
{"Gureadomosu", true},
|
||||
{"Harudomerugu", true},
|
||||
{"Toridcless", true},
|
||||
{"Gasurabazura", true},
|
||||
{"Kusubami", false},
|
||||
{"Yama Kurai", true},
|
||||
{"2nd Block Duremudira", true},
|
||||
{"Zinogre", true},
|
||||
{"Deviljho", true},
|
||||
{"Brachydios", true},
|
||||
{"Berserk Raviente", true},
|
||||
{"Toa Tesukatora", true},
|
||||
{"Barioth", true},
|
||||
{"Uragaan", true},
|
||||
{"Stygian Zinogre", true},
|
||||
{"Guanzorumu", true},
|
||||
{"Savage Deviljho", true}, // + Starving/Heavenly
|
||||
{"Mon156", false},
|
||||
{"Egyurasu", false},
|
||||
{"Voljang", true},
|
||||
{"Nargacuga", true},
|
||||
{"Keoaruboru", true},
|
||||
{"Zenaserisu", true},
|
||||
{"Gore Magala", true},
|
||||
{"Blinking Nargacuga", true},
|
||||
{"Shagaru Magala", true},
|
||||
{"Amatsu", true},
|
||||
{"Eruzerion", true}, // + Musou
|
||||
{"Musou Duremudira", true},
|
||||
{"Mon168", false},
|
||||
{"Seregios", true},
|
||||
{"Bogabadorumu", true},
|
||||
{"Mon171", false},
|
||||
{"Musou Bogabadorumu", true},
|
||||
{"Costumed Uruki", false},
|
||||
{"Musou Zerureusu", true},
|
||||
{"Rappy", false},
|
||||
{"King Shakalaka", false},
|
||||
}
|
||||
50
utils/pascalstring/pascalstring.go
Normal file
50
utils/pascalstring/pascalstring.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package pascalstring
|
||||
|
||||
import (
|
||||
"erupe-ce/utils/byteframe"
|
||||
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func Uint8(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
bf.WriteUint8(0)
|
||||
return
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint8(uint8(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
|
||||
func Uint16(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
bf.WriteUint16(0)
|
||||
return
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint16(uint16(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
|
||||
func Uint32(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
bf.WriteUint32(0)
|
||||
return
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint32(uint32(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
38
utils/stringstack/stringstack.go
Normal file
38
utils/stringstack/stringstack.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package stringstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// StringStack is a basic LIFO "stack" for storing strings.
|
||||
type StringStack struct {
|
||||
stack []string
|
||||
}
|
||||
|
||||
// New creates a new instance of StringStack
|
||||
func New() *StringStack {
|
||||
return &StringStack{}
|
||||
}
|
||||
|
||||
// Set sets up a new StringStack
|
||||
func (s *StringStack) Set(v string) {
|
||||
s.stack = []string{v}
|
||||
}
|
||||
|
||||
// Push pushes a string onto the stack.
|
||||
func (s *StringStack) Push(v string) {
|
||||
s.stack = append(s.stack, v)
|
||||
}
|
||||
|
||||
// Pop pops a string from the stack.
|
||||
func (s *StringStack) Pop() (string, error) {
|
||||
var x string
|
||||
if len(s.stack) == 0 {
|
||||
return x, errors.New("no items on stack")
|
||||
}
|
||||
|
||||
x = s.stack[len(s.stack)-1]
|
||||
s.stack = s.stack[:len(s.stack)-1]
|
||||
|
||||
return x, nil
|
||||
}
|
||||
136
utils/stringsupport/string_convert.go
Normal file
136
utils/stringsupport/string_convert.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package stringsupport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func UTF8ToSJIS(x string) []byte {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return []byte(xt)
|
||||
}
|
||||
|
||||
func SJISToUTF8(b []byte) string {
|
||||
d := japanese.ShiftJIS.NewDecoder()
|
||||
result, err := io.ReadAll(transform.NewReader(bytes.NewReader(b), d))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func ToNGWord(x string) []uint16 {
|
||||
var w []uint16
|
||||
for _, r := range []rune(x) {
|
||||
if r > 0xFF {
|
||||
t := UTF8ToSJIS(string(r))
|
||||
if len(t) > 1 {
|
||||
w = append(w, uint16(t[1])<<8|uint16(t[0]))
|
||||
} else {
|
||||
w = append(w, uint16(t[0]))
|
||||
}
|
||||
} else {
|
||||
w = append(w, uint16(r))
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func PaddedString(x string, size uint, t bool) []byte {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
return make([]byte, size)
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
out := make([]byte, size)
|
||||
copy(out, x)
|
||||
out[len(out)-1] = 0
|
||||
return out
|
||||
}
|
||||
|
||||
func CSVAdd(csv string, v int) string {
|
||||
if len(csv) == 0 {
|
||||
return strconv.Itoa(v)
|
||||
}
|
||||
if CSVContains(csv, v) {
|
||||
return csv
|
||||
} else {
|
||||
return csv + "," + strconv.Itoa(v)
|
||||
}
|
||||
}
|
||||
|
||||
func CSVRemove(csv string, v int) string {
|
||||
s := strings.Split(csv, ",")
|
||||
for i, e := range s {
|
||||
if e == strconv.Itoa(v) {
|
||||
s[i] = s[len(s)-1]
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
}
|
||||
return strings.Join(s, ",")
|
||||
}
|
||||
|
||||
func CSVContains(csv string, v int) bool {
|
||||
s := strings.Split(csv, ",")
|
||||
for i := 0; i < len(s); i++ {
|
||||
j, _ := strconv.ParseInt(s[i], 10, 32)
|
||||
if int(j) == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CSVLength(csv string) int {
|
||||
if csv == "" {
|
||||
return 0
|
||||
}
|
||||
s := strings.Split(csv, ",")
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func CSVElems(csv string) []int {
|
||||
var r []int
|
||||
if csv == "" {
|
||||
return r
|
||||
}
|
||||
s := strings.Split(csv, ",")
|
||||
for i := 0; i < len(s); i++ {
|
||||
j, _ := strconv.ParseInt(s[i], 10, 32)
|
||||
r = append(r, int(j))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func CSVGetIndex(csv string, i int) int {
|
||||
s := CSVElems(csv)
|
||||
if i < len(s) {
|
||||
return s[i]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func CSVSetIndex(csv string, i int, v int) string {
|
||||
s := CSVElems(csv)
|
||||
if i < len(s) {
|
||||
s[i] = v
|
||||
}
|
||||
var r []string
|
||||
for j := 0; j < len(s); j++ {
|
||||
r = append(r, fmt.Sprintf(`%d`, s[j]))
|
||||
}
|
||||
return strings.Join(r, ",")
|
||||
}
|
||||
23
utils/token/token.go
Normal file
23
utils/token/token.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
var RNG = NewRNG()
|
||||
|
||||
// Generate returns an alphanumeric token of specified length
|
||||
func Generate(length int) string {
|
||||
var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
b := make([]rune, length)
|
||||
for i := range b {
|
||||
b[i] = chars[RNG.Intn(len(chars))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// NewRNG returns a new NewRNG generator
|
||||
func NewRNG() *rand.Rand {
|
||||
return rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
}
|
||||
Reference in New Issue
Block a user