News: 11 March 2016 - Forum Rules
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia, Danke

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Nanashi3

Pages: [1]
1
Newcomer's Board / Re: Shining Tears UNDUB attempt
« on: January 06, 2014, 04:52:44 pm »
Hey @Sizzin,

You may want to have a look at roxfan's cvm tool (mirror) to convert back and forth between .CVM and .ISO
Quote
cvm_tool.exe
ROFS tool v0.02 by roxfan (c) 2010.
Usage: cvm_tool [options] <command> <file1>...
    available commands:
    info  [-p <password>] <file.cvm>                          Show information about a ROFS volume
    split [-p <password>] <file.cvm> <file.iso> [<file.hdr>]  Extract ISO file from a ROFS volume
    mkcvm [-p <password>] <file.cvm> <file.iso>  <file.hdr>   Make a ROFS volume from an ISO file and header file
Do not mind the password option, since we are not dealing with encrypted (obfuscated) volumes.

Here is the tool output:
Quote
cvm_tool split H:\ST.CVM ST.CVM.iso ST.CVM.hdr
Input file: H:\ST.CVM
00000000: chunk 'CVMH', length 0x000007F4 (0x00000800)
CVMH chunk:
  file size: 0x5C375000
  date: 2004-12-27 19:45:12, GMT offset: 540 minutes
  verinfo1: 1.1.0.0
  flags_30: 0x00000000
  FS id: ROFS
  maker id: 'ROFSBLD Ver.1.52 2003-06-09'
  verinfo2: 1.31.0.0
  flag_7C: 3
  flag_7D: 0
  zone table (1 entries): [ 1 ]
  zone TOC sector: 1 (index 0)
  ISO start sector: 3
00000800: chunk 'ZONE', length 0x5C3747F4 (0x5C374800)
ZONE chunk:
  zone0C: 3
  zone10: 0
  zone11: 0
  zone12: 4
  zone13: 16
  zone14: 0
  sector len 1: 2048
  sector len 2: 2048
  dataloc1: sector 2, len 0x00000800
  dataloc ISO: sector 3, len 0x5C373800
Output file: ST.CVM.iso
Header file: ST.CVM.hdr

cvm_tool mkcvm ST-gen.CVM ST.CVM.iso ST.CVM.hdr
Input file: ST.CVM.iso
Header file: ST.CVM.hdr
Writing unencrypted volume
Output file: ST-gen.CVM
Patching ISO zone length to 0x5C373800
Patching file size to 0x5C375000
Setting encryption flag to 0

sha1sum H:\ST.CVM ST-gen.CVM
5624073167a074bbcd596b47f88a5a99eb4e2021  H:\ST.CVM
5624073167a074bbcd596b47f88a5a99eb4e2021  ST-gen.CVM


The CVM (ISO9660) for the USA version has the following structure: https://snipt.net/raw/0631ca7d35f144c9487a17f5f74d226f/?nice (listing mirror)


Here are a few observations on the matter:

- There are noticeably fewer .AHX + .ADX files than actually spoken in-game; where are they buried? [in both USA and JPN editions]. I also saw a few structures resembling ADPCM-like sounds
- TXR looks like a bitmap format that has several flavors, 5bpp, 8bpp indexed RGBa, flat 32bpp RGBa, and there are occasionally several images in a single TXR.
- RAX is an archive format containing several of the following filetypes: txr, ban, trs, efp, etc. It employs a clearly marked LZSS compression variant which I have not completely reversed. The window is 0x2000 bytes long, i.e. 13-bit long backreference index. It is kinda weird because the command word is also used as a source, meaning you read 5 bits from the control word and 8 bits from the curret byte to compute the backreference offset.
If you want to have a look, go to sub_14A340 in SLUS-21063, sub_14A310 in SLPM-65773, sub_221FF0 in SLPM-66671

- some files entries in PACK***.RAX match other files in the CVM hierarchy, e.g. AHX/*
- Shining Wind is the other title in the series using the same engine.

Appendices

NON-WORKING TXR converter:
Code: [Select]
# Extractor for SEGA/Nextech *.TXR (script 0.1.0)
# = used in shining tears & shining wind
# script for QuickBMS http://quickbms.aluigi.org

endian little
get myext EXTENSION
if myext != "TXR"
    print "Please run this script on a .TXR file"
    cleanexit
endif

get DAT_SIZE asize

# A .txr file typically contains a single bitmap, but there are some edge cases
math i = 0

for OFFSET = 0 < DAT_SIZE
    idstring "TXR\x00"

    math i += 1
   
    get query->bm_type long
    get query->bm_widt short
    get query->bm_heig short
    get RESERVED_ long # always zero?

    callfunction query2tga
    cleanexit

    savepos OFFSET
next

###############################################
# struct TGAHeader
# {
#   uint8   idLength,           // Length of optional identification sequence.
#           paletteType,        // Is a palette present? (1=yes)
#           imageType;          // Image data type (0=none, 1=indexed, 2=rgb,
#                               // 3=grey, +8=rle packed).
#   uint16  firstPaletteEntry,  // First palette index, if present.
#           numPaletteEntries;  // Number of palette entries, if present.
#   uint8   paletteBits;        // Number of bits per palette entry.
#   uint16  x,                  // Horiz. pixel coord. of lower left of image.
#           y,                  // Vert. pixel coord. of lower left of image.
#           width,              // Image width in pixels.
#           height;             // Image height in pixels.
#   uint8   depth,              // Image color depth (bits per pixel).
#           descriptor;         // Image attribute flags.
# };
startfunction query2tga

    savepos DATOFFSET

    if query->bm_type == 0
        # 32bpp RGBa
        math query->bm_bpp = 32
        math query->bm_pal = 0
        set MEMORY_FILE binary "\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xAA\xAA\xBB\xBB\x20\x28"
    elif query->bm_type == 0x13
        # indexed 8bpp RGBa with 256-entry RGBa palette (0x400 bytes)
        math query->bm_bpp = 8
        math query->bm_pal = 0x400
        set MEMORY_FILE binary "\x00\x01\x01\x00\x00\x00\x01\x20\x00\x00\x00\x00\xAA\xAA\xBB\xBB\x08\x28"
#    elif query->bm_type == 0x14
        # 5bpp
    else
        print "unknown bitmap type in TXR file %query->bm_type% starting @ offset %OFFSET%"
        cleanexit
    endif

    goto 0x0C MEMORY_FILE   # x,y
    put query->bm_widt short MEMORY_FILE
    put query->bm_heig short MEMORY_FILE
    get TGAHDSIZE asize MEMORY_FILE

    math PXBITMAP = query->bm_widt
    math PXBITMAP *= query->bm_heig
    math BYT = query->bm_bpp
    math BYT u/= 8
    math PXBITMAP *= BYT

    get NAME basename
    string NAME p= "%s_%03d.tga" NAME i
append
    log MEMORY_FILE2 0 TGAHDSIZE MEMORY_FILE
    if query->bm_pal > 0
        math PALOFFSET = DATOFFSET
        math PALOFFSET += PXBITMAP  # TXR puts image data first, then colormap
        log MEMORY_FILE2 PALOFFSET query->bm_pal
    endif

    log MEMORY_FILE2 DATOFFSET PXBITMAP
append
    get TGATOTSIZE asize MEMORY_FILE2
    log NAME 0 TGATOTSIZE MEMORY_FILE2

endfunction

Blue-ish Elwyn
 


Some LZSS sample (extracted out of a .RAX file):



001.BAN.slz = 0x3AE4 bytes
001.BAN = 0xFA90 bytes


EDIT1

The initial scene where Elwyn finds a boy washed ashore = event/TOWN02.bss from PACKTOWN02.RAX
A .bss file is a compiled script made of 4 sections:
- TNVE "EVeNT" seems to be a bunch of offsets
- CNUF "FUNCtions", a bunch of ASCII symbols
- DCMV "VM CoDe"
- TDMV "VM DaTa" = \x00 separated list of strings
- DEMV (end of file marker)

You shall find this file in-memory when snapping a savestate.

The DCMV and TDMV are of particular interest to us:
- DCMV contains opcodes referring to TDMV strings by offset, after removing the 12-byte long header
- TDMV contains game strings (ASCII strings in the case of the USA version)

e.g.:
  • offset 678B: Looks like he's hurt pretty bad...
  • offset 67AF: Boy (speaker name)
  • offset 67B3: U... Ugh...
  • offset 67C0: I better get him to (...)

contrast with JPN version:


  • offset 59E3: *S4*ひどい傷……
  • offset 59F9: 少年 (speaker name)
  • offset 59FE: *S5*う…… (...)
  • offset 5A28: *S4*一刻も早く^ (...)

Undoubtedly some of those opcodes in the JPN version correspond to voiceovers.
If you compare USA vs. JPN, there are outstanding instructions on the right column:
USA                           

27: FFFFFFFF (-1)             
29: 08000000 (8)             





2A: 8B670000 (ofs 678B) msgbox                       
29: 13000000 (19)             
29: 09000000 (9)             
02: 01000000 (1)
02: 02000000 (2)
02: 18000000 (24)
27: FDFFFFFF (-3)
29: 0A000000 (10)
02: 02000000 (2)             
03: AF670000 (ofs 67AF) msgspk
27: FEFFFFFF (-2)             
29: 0F000000 (15)             
02: FFFFFFFF (-1)             
27: FFFFFFFF (-1)             
29: 08000000 (8)             
                             
                             
                             
                             
                             
2A: B3670000 (ofs 67B3) msgbox
29: 13000000 (19)             
29: 09000000 (9)             
02: 01000000 (1)             
02: 02000000 (2)             
02: 19000000 (31)             
27: FDFFFFFF (-3)             
29: 0A000000 (10)             
02: 01000000 (1)             
27: FFFFFFFF (-1)             
29: 08000000 (8)             
                             
                             
                             
                             
                             
2A: C0670000 (ofs 67C0) msgbox
JPN

                 27: FFFFFFFF (-1)
                 29: 08000000 (8)
02: 2C000000 (44)
02: 03000000 (3)
02: 00000000 (0)
27: FDFFFFFF (-3)
29: 39000000 (57)

                 2A: E3590000 (ofs 59E3) msgbox
                 29: 10000000 (16)
                 29: 09000000 (9)





                 02: 02000000 (2)
                 03: F9590000 (ofs 59F9) msgspk
                 27: FEFFFFFF (-2)
                 29: 0C000000 (12)
                 02: FFFFFFFF (-1)
                 27: FFFFFFFF (-1)
                 29: 08000000 (8)
02: 2D000000 (45)
02: 03000000 (3)
02: 00000000 (0)
27: FDFFFFFF (-3)
29: 39000000 (57)

                 2A: FE590000 (ofs 59FE) msgbox
                 29: 10000000 (16)
                 29: 09000000 (9)
                 02: 01000000 (1)
                 02: 02000000 (2)
                 02: 19000000 (31)
                 27: FDFFFFFF (-3)
                 29: 0A000000 (10)
                 02: 01000000 (1)
                 27: FFFFFFFF (-1)
                 29: 08000000 (8)
02: 2E000000 (46)
02: 03000000 (3)
02: 00000000 (0)
27: FDFFFFFF (-3)
29: 39000000 (57)

                 2A: 285A0000 (ofs 5A28) msgbox


EDIT2:
OK, I have confirmed by loading a save state, that the incrementing value refers to a voice file.
The 27 opcode seems to indicate how many values need to be "unstacked" by the following function call (-1, -2, -3, etc.)
The 29 opcode, subcode 39 seems to be the code for playing a voice file, taking 3 parameters.

Now what's left:
1/ somehow recompile a valid bss image, fixing pointers
2/ repack a RAX archive. Thoroughly figuring the LZSS compression is a plus, but not necessary, as a dummy compression always reading literals (0xFF control word) should be enough.
2a/ which copy takes precedence in the CVM? The one in the ISO9660 or in the RAX?
3/ recreate ST.CVM
4/ repack complete ISO, test


Appendix

RAX archive dumper:
Code: [Select]
# Dumper for SEGA/Nextech *.RAX (script 0.1.0)
# = used in shining tears & shining wind
# script for QuickBMS http://quickbms.aluigi.org

# This version only dumps LZSS segments as-is

endian little
get myname FILENAME
get myext EXTENSION
if myext != "RAX"
    print "Please run this script on a .RAX file"
    cleanexit
endif

get DAT_SIZE asize
idstring "RAX\x00"
get rax->RESERVED1_ long
get rax->RESERVED2_ long
get rax->entcount long

math i = 0

for OFFSET = 0x10 < DAT_SIZE
    getdstring query->sig 4
    get        query->blksize long
    get        query->fnamelen long
    get        query->usize long

    math i += 1

    if query->fnamelen > 0x100
        print "(%myname% at OFFSET=%OFFSET%) entry filename overly long, maybe bad value [query->fnamelen=%query->fnamelen%]"
        cleanexit
    endif
    if query->fnamelen > 0
        getdstring query->fname query->fnamelen
    else
        string query->fname p= "--%03d--.bin" i
    endif

    set        query->zsize query->blksize
    math query->zsize -= 0x10
    math query->zsize -= query->fnamelen

    savepos tmpOFFSET
    get magic long
    goto tmpOFFSET
   
    string dumpext = ".slz"
    if magic u!= 0x53535A4C # LZSS
        string dumpext = ".raw"
    endif

    set fname query->fname
    string fname R= "/" "!"
    string fname R= "\\" "!"

    get NAME basename
    string NAME p= "%s# [%s]%s" NAME fname dumpext
   
    if magic u== 0x53535A4C # LZSS
        log NAME tmpOFFSET query->zsize
    else
        log NAME tmpOFFSET query->usize
    endif

    goto query->zsize 0 SEEK_CUR

    savepos OFFSET
next

if i != rax->entcount
    print "(%myname%) Expected %rax->entcount% entries, found %i%"
endif

_ext_rax.cmd helper batch (copy cvm contents into work/ and duplicate its tree structure in extr/) :
Code: [Select]
@echo off & setlocal ENABLEEXTENSIONS

goto :main

:do_one
SET ZPATH=%~dp1
SET ZPATH=%ZPATH:work=extr%
quickbms -o sega-nextech-shining_wind-rax-010.bms %1 %ZPATH%
goto :eof

:main
for /R %%i in (*.rax) DO call :do_one %%i



:end
pause
EDIT3:
I've been leading a few interesting experiments on the NTSC-J version.

(...put in a separate entry b/c this post exceeds the board limit...)
main conclusions are:
- can mod RAX files
- CVMFS takes over if entry missing from RAX

Pages: [1]