refactored gametime and utils folder which decouples entrance and sign from channel servers

This commit is contained in:
stratic-dev
2024-10-09 08:34:13 +01:00
parent 4348aa02a8
commit e8fc650d49
496 changed files with 1484 additions and 1443 deletions

8
utils/bfutil/bfutil.go Normal file
View 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
View 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.

View 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
View 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
}

View 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
View 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
}

View 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
View 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
View 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},
}

View 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))
}

View 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
}

View 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
View 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()))
}