Skip to content

Commit

Permalink
Merge pull request #8 from great-majority/feat/honeycome
Browse files Browse the repository at this point in the history
Feat/honeycome
  • Loading branch information
great-majority authored Aug 13, 2023
2 parents 33773c6 + f0763bd commit eb02cbe
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 11 deletions.
18 changes: 17 additions & 1 deletion README.ja.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# KoikatuCharaLoader
このプログラムは、コイカツやエモクリのキャラカードをPythonで読み込む・書き込むためのライブラリです。(キャラカードの他にもセーブデータ等も完全ではないですが読み込めます)
このプログラムは、コイカツ・エモクリ・ハニカムのキャラカードをPythonで読み込む・書き込むためのライブラリです。(キャラカードの他にもセーブデータ等も完全ではないですが読み込めます)

[![](https://img.shields.io/pypi/v/kkloader)](https://pypi.org/project/kkloader/)
[![Downloads](https://pepy.tech/badge/kkloader)](https://pepy.tech/project/kkloader)
Expand All @@ -26,6 +26,19 @@ $ python
```
簡単!

# 使用できるクラスの一覧

- 読み込みと書き込み両方に対応
- KoikatuCharaData
- EmocreCharaData
- HoneycomeCharaData
- 読み込みのみ対応
- KoikatuSaveData
- EmocreMapData
- EmocreSceneData

いずれのクラスも `from kkloader import KoikatuCharaData` のようにインポートし、 `.load(filename)` のようにファイルを読み込むことができます。

# ブロックデータについて

コイカツのキャラデータは"ブロックデータ"というデータのかたまりから成っています。
Expand Down Expand Up @@ -165,3 +178,6 @@ kc.save("./data/kk_chara_modified.png")

# 謝辞
- [martinwu42/pykoikatu](https://github.com/martinwu42/pykoikatu)

# 連絡先
[@tropical_362827](https://twitter.com/tropical_362827)
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# KoikatuCharaLoader
A simple deserializer / serializer for Koikatu / EmotionCreators character data.
A simple deserializer / serializer for Koikatu / EmotionCreators / Honeycome character data.

[![](https://img.shields.io/pypi/v/kkloader)](https://pypi.org/project/kkloader/)
[![Downloads](https://pepy.tech/badge/kkloader)](https://pepy.tech/project/kkloader)
Expand Down Expand Up @@ -28,6 +28,19 @@ $ python
```
that's it :)

# List of Classes

- Supports saving and loading
- KoikatuCharaData
- EmocreCharaData
- HoneycomeCharaData
- Supports loading only
- KoikatuSaveData
- EmocreMapData
- EmocreSceneData

Any class can be imported like `from kkloader import KoikatuCharaData` and data can be loaded using the `.load(filename)` method.

# Mechanism of the Blockdata

A character data of koikatu consists of some *blockdata*.
Expand Down Expand Up @@ -163,3 +176,7 @@ kc.save("./data/kk_chara_modified.png")

# Acknowledgements
- [martinwu42/pykoikatu](https://github.com/martinwu42/pykoikatu)

# Contact

[@tropical_362827](https://twitter.com/tropical_362827)
Binary file added data/hc_chara.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion kkloader/EmocreCharaData.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import struct

import kkloader
import kkloader.KoikatuCharaData
from kkloader.funcs import get_png, load_length, load_type


class EmocreCharaData(kkloader.KoikatuCharaData):
def __init__(self):
pass
self.modules = {
"Custom": kkloader.kk_Custom,
"Coordinate": kkloader.kk_Coordinate,
"Parameter": kkloader.kk_Parameter,
"Status": kkloader.kk_Status,
"About": kkloader.kk_About,
"KKEx": kkloader.kk_KKEx,
}

def _load_header(self, data, **kwargs):
self.image = get_png(data)
Expand Down
96 changes: 96 additions & 0 deletions kkloader/HoneycomeCharaData.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import io
import struct

import kkloader.KoikatuCharaData
from kkloader.funcs import load_length, msg_pack, msg_unpack
from kkloader.KoikatuCharaData import BlockData


class HoneycomeCharaData(kkloader.KoikatuCharaData):
def __init__(self):
self.modules = {
"Custom": Custom,
"Coordinate": Coordinate,
"Parameter": kkloader.kk_Parameter,
"Status": kkloader.kk_Status,
"Graphic": Graphic,
"About": kkloader.kk_About,
"GameParameter_HCP": GameParameter_HCP,
"GameInfo_HCP": GameInfo_HCP,
}


class Custom(BlockData):
fields = ["face", "body"]

def __init__(self, data, version):
self.name = "Custom"
self.version = version
self.data = {}
data_stream = io.BytesIO(data)
for f in self.fields:
self.data[f] = msg_unpack(load_length(data_stream, "i"))

def serialize(self):
data = []
pack = struct.Struct("i")
for f in self.fields:
field_s, length = msg_pack(self.data[f])
data.append(pack.pack(length))
data.append(field_s)
serialized = b"".join(data)
return serialized, self.name, self.version


class Coordinate(BlockData):
fields = [
"clothes",
"accessory",
"makeup",
"hair",
"nail",
]

def __init__(self, data, version):
self.name = "Coordinate"
self.version = version
if data is None:
return

self.data = []
for coordinate_bytes in msg_unpack(data):
data_stream = io.BytesIO(coordinate_bytes)
coordinate_dict = {}
for f in self.fields:
coordinate_dict[f] = msg_unpack(load_length(data_stream, "i"))
self.data.append(coordinate_dict)

def serialize(self):
data = []
for i in self.data:
c = []
pack = struct.Struct("i")

for f in self.fields:
serialized, length = msg_pack(i[f])
c.extend([pack.pack(length), serialized])

data.append(b"".join(c))
serialized_all, _ = msg_pack(data)

return serialized_all, self.name, self.version


class Graphic(BlockData):
def __init__(self, data, version):
super().__init__(name="Graphic", data=data, version=version)


class GameParameter_HCP(BlockData):
def __init__(self, data, version):
super().__init__(name="GameParameter_HCP", data=data, version=version)


class GameInfo_HCP(BlockData):
def __init__(self, data, version):
super().__init__(name="GameInfo_HCP", data=data, version=version)
15 changes: 10 additions & 5 deletions kkloader/KoikatuCharaData.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ def bin_to_str(serial):


class KoikatuCharaData:
readable_formats = ["Custom", "Coordinate", "Parameter", "Status", "About", "KKEx"]

def __init__(self):
pass
self.modules = {
"Custom": Custom,
"Coordinate": Coordinate,
"Parameter": Parameter,
"Status": Status,
"About": About,
"KKEx": KKEx,
}

@classmethod
def load(cls, filelike, contains_png=True):
Expand Down Expand Up @@ -68,8 +73,8 @@ def _load_blockdata(self, data):
data = lstinfo_raw[pos : pos + size]

self.blockdata.append(name)
if name in self.readable_formats:
setattr(self, name, globals()[name](data, version))
if name in self.modules.keys():
setattr(self, name, self.modules[name](data, version))
else:
setattr(self, name, UnknownBlockData(name, data, version))
self.unknown_blockdata.append(name)
Expand Down
11 changes: 10 additions & 1 deletion kkloader/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
from .KoikatuCharaData import * # noqa isort: skip
from .KoikatuCharaData import ( # noqa isort: skip
Custom as kk_Custom,
Coordinate as kk_Coordinate,
Parameter as kk_Parameter,
About as kk_About,
Status as kk_Status,
KKEx as kk_KKEx,
KoikatuCharaData,
)
from .KoikatuSaveData import KoikatuSaveData # noqa isort: skip
from .EmocreCharaData import EmocreCharaData # noqa
from .EmocreMapData import EmocreMapData # noqa
from .EmocreSceneData import EmocreSceneData # noqa
from .HoneycomeCharaData import HoneycomeCharaData # noqa
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "kkloader"
version = "0.0.0"
description = "a simple deserializer / serializer for Koikatu / EmotionCreators data."
authors = ["great-majority <yosaku.ideal+github@gmail.com>"]
authors = ["great-majority <wired.wireless0@gmail.com>"]
homepage = "https://github.com/great-majority/KoikatuCharaLoader"
readme = "README.md"

Expand Down
25 changes: 24 additions & 1 deletion test/test_charadata.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import tempfile

from kkloader import EmocreCharaData, KoikatuCharaData
from kkloader import EmocreCharaData, HoneycomeCharaData, KoikatuCharaData


def test_load_character():
Expand Down Expand Up @@ -37,6 +37,12 @@ def test_load_mod_character():
assert hasattr(kc, "KKEx")


def test_load_honeycome_character():
hc = HoneycomeCharaData.load("./data/hc_chara.png")
for f in hc.modules.keys():
assert hasattr(hc, f)


def test_save_character():
tmpfile = tempfile.NamedTemporaryFile()
kc = KoikatuCharaData.load("./data/kk_chara.png")
Expand Down Expand Up @@ -64,6 +70,16 @@ def test_save_emocre_character():
assert bytes(ec) == bytes(ec2)


def test_save_honeycome_character():
tmpfile = tempfile.NamedTemporaryFile()
hc = HoneycomeCharaData.load("./data/hc_chara.png")
hc.save(tmpfile.name)
hc2 = HoneycomeCharaData.load(tmpfile.name)
assert hc["Parameter"]["lastname"] == hc2["Parameter"]["lastname"]
assert hc["Parameter"]["firstname"] == hc2["Parameter"]["firstname"]
assert bytes(hc) == bytes(hc2)


def test_json_character():
kc = KoikatuCharaData.load("./data/kk_chara.png")
tmpfile = tempfile.NamedTemporaryFile()
Expand All @@ -80,3 +96,10 @@ def test_json_emocre_character():
ec = EmocreCharaData.load("./data/ec_chara.png")
tmpfile = tempfile.NamedTemporaryFile()
ec.save_json(tmpfile.name)


def test_json_honeycome():
hc = HoneycomeCharaData.load("./data/hc_chara.png")
tmpfile = tempfile.NamedTemporaryFile()
hc.save_json("test.json")
hc.save_json(tmpfile.name)

0 comments on commit eb02cbe

Please sign in to comment.