第七史詩文件spine解密相關

請教大佬們我這裡有spine解密時和加密時候的文件
請教大佬們有辦法解密嗎
melissa.rar (1.1 MB)
json對應scsp
png對應sct
再麻煩大佬了們了

scsp使用lz4.block压缩, 第1个4字节是解压缩大小, 第2个4字节是数据长度
解压后的数据是自定义格式, 它魔改了spine 2.1.27和3.8的格式, 你需要自己解析并还原

1 个赞

my personal files, missing NPC characters mellisa == c1096

6 个赞

06

感谢分享
“spine”: “2.1.27.scsp” 改如何打开, 或者如何还原

同求 spine”: “2.1.27.scsp 还原方法
12

哪为大佬能提供一个 json 格式的正常的spine”: “2.1“文件,用来研究正常的spine”: “2.1“ 与“spine”: “2.1.27.scsp” 的差异

询问一下那sct文件怎么转换

from pathlib import Path
from io import BytesIO, BufferedReader
from lz4.block import decompress as decLz4Block
from PIL.Image import Image, frombuffer as imgfb
from numpy import frombuffer as npfb, stack as npst, uint8, uint16

class SctToPng:
    Sign: bytes = b'SCT\x01'
    SignLen: int = len(Sign)

    @classmethod
    def Cov(cls, stream: BytesIO|BufferedReader) -> Image:
        if stream.read(cls.SignLen) != cls.Sign:
            raise ValueError('Sign 错误')
        texType = stream.read(1)[0]
        width = int.from_bytes(stream.read(2), 'little')
        height = int.from_bytes(stream.read(2), 'little')
        decLen = int.from_bytes(stream.read(4), 'little')
        comLen = int.from_bytes(stream.read(4), 'little')
        data = decLz4Block(stream.read(comLen), uncompressed_size=decLen)
        if texType == 2:
            return imgfb('RGBA', (width, height), data[:width*height*4], 'raw')
        elif texType == 4:
            start = width * height * 2
            rgb = npfb(data[:start], dtype=uint16).reshape((height, width))
            a = npfb(data[start:start+(width*height)], dtype=uint8).reshape((height, width))
            r = (((((rgb >> 11) & 0x1F) * 527) + 23) >> 6).astype(uint8)
            g = (((((rgb >> 5) & 0x3F) * 259) + 33) >> 6).astype(uint8)
            b = ((((rgb & 0x1F) * 527) + 23) >> 6).astype(uint8)
            return imgfb('RGBA', (width, height), npst([r, g, b, a], axis=-1).tobytes(), 'raw')
        else:
            raise ValueError('贴图格式 错误')

    @classmethod
    def Read(cls, file: bytes|bytearray|str|Path|BytesIO|BufferedReader) -> Image:
        if isinstance(file, str|Path):
            with open(file, 'rb') as f:
                return cls.Cov(f)
        elif isinstance(file, (bytes, bytearray)):
            return cls.Cov(BytesIO(file))
        else:
            return cls.Cov(file)

def batch(datPath: str = '', subfolder: bool = False):
    path = Path(datPath) if datPath else Path.cwd()
    need = [i for i in (path.rglob('*.sct') if subfolder else path.glob('*.sct')) if i.is_file()]
    for i in need:
        try:
            img = SctToPng.Read(i)
        except Exception as e:
            print(f'转换错误 --- {i.as_posix()} --- {e}')
            continue
        img.save(f'{i.as_posix()[:-3]}png')

if __name__ == '__main__':
    path = r'' # sct文件所在的路径
    subfolder = False # 是否查找字文件夹内的文件
    batch(path, subfolder)
3 个赞

结构如下
4 bytes - 标识
1 bytes(uint8) - 贴图类型 2-RGBA8888, 4-RGB565A8, 其它类型没遇到
2 bytes(uint16) - 贴图宽度
2 bytes(uint16) - 贴图高度
4 bytes(uint32) - 数据解压大小
4 bytes(uint32) - 数据压缩大小

根据上述结构读取文件
然后根据 读取压缩大小的字节然后用 lz4.block 传递解压大小解压这部分数据, 然后将解压缩后的数据转换为图像

1 个赞

好的感谢大佬!scsp那个目前有办法解析还原吗?按照大佬你那个意思,我目前也是卡在了解析还原的阶段

它不是简单的魔改, 而是重构了整个spine格式, 要还原相当麻烦

如果你不熟悉spine的所有属性, 那还原就更加麻烦, 因为它的值非常的多, 需要靠经验判断哪一段是spine的属性

这样啊好吧,那我再研究研究,感谢大佬

论坛里的 Live2d-SpineViewer有转换功能…

我们说的是scsp文件不是已经转换好的文件,你可以下载楼主那个压缩包就知道了

EpicSevenTools.zip (6.5 KB)
写个半成品目前可以转换slot和bones不知有无大蛇来完善一下,这东西目测应该算是skel的一个变种

请问scsp前8字节的读取是用什么模式,我看上面那位的例子里直接用BinaryReader.ReadInt32,是小端有符号int吗?为什么我只有大端无符号Int解压lz4成功了,而且解压后大小和给出的解压后大小对不上

发重了

两个小端int32 第一个是解压前的大小第二个是解压后的大小