Skip to content
View in the app

A better way to browse. Learn more.

ResHax

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
Help us keep the site running.

Recolove Model Importer (Noesis)

Featured Replies

  • Author
  • Localization

kana4567, posted Fri Jun 08, 2018 11:51 am (35727)


Here is Recolove Model Importer for Noesis.
It loads mesh data (vertex, face, normals and UVs) from a *.fed model.

fmt_recolove_fed.py:
Code:
# v2: Fixed face direction and UV
import math
from inc_noesis import *


def registerNoesisTypes():
    """ register plugin
    """
    handle = noesis.register("Recolove Model", ".fed")
    noesis.setHandlerTypeCheck(handle, fedCheckType)
    noesis.setHandlerLoadModel(handle, fedLoadModel)

    # noesis.logPopup()  # show console
    return 1


def fedCheckType(data):
    """ check header
    """
    FED_HEADER = 0x43415046  # FPAC
    bs = NoeBitStream(data)
    bs.seek(0)
    if bs.readInt() != FED_HEADER:
        return 0
    return 1


def fedLoadModel(data, mdlList):
    """ load model (mesh and UV)
    """
    bs = NoeBitStream(data)
    meshes = []
    myMeshes = []

    # find face sets. eg. (0, 1, 2)
    bs.seek(0)
    while not bs.checkEOF():
        firstFace = (bs.readUShort(), bs.readUShort(), bs.readUShort())
        tailVals = (bs.readUShort(), bs.readUShort(), bs.readUShort(),
                    bs.readUShort(), bs.readUShort())
        if (firstFace == (0, 1, 2) and sum(tailVals) < 30):
            myMeshes.append({"faceOffset": bs.tell() - 16})
    if len(myMeshes) == 0:
        print("no face sets.")
        return 0

    # find face counts
    for myMesh in myMeshes:
        isFound = False
        ofs = myMesh["faceOffset"]
        while True:
            ofs -= 16  # go up
            if isFound:
                break
            if ofs < 0:
                print("face count not found.")
                return 0
            bs.seek(ofs)
            numFaceX3 = bs.readUShort()
            bs.readUShort()  # unknown
            magic1C = bs.readInt()
            magicZero1 = bs.readInt64()
            numVert = bs.readInt()
            magicZero2 = bs.readInt()
            magicZero3 = bs.readInt64()
            if (numFaceX3 % 3 == 0 and magic1C == 0x1C and magicZero1 == 0 and magicZero2 == 0 and magicZero3 == 0):
                isFound = True
                myMesh["numFace"] = numFaceX3 // 3
                myMesh["numVert"] = numVert
                myMesh["vertOffset"] = bs.tell()

    for myMesh in myMeshes:
        print(myMesh)

        verts = []
        bs.seek(myMesh["vertOffset"])
        for i in range(myMesh["numVert"]):
            x = _getFloat(bs)
            y = _getFloat(bs)
            z = _getFloat(bs)
            verts.append(NoeVec3((x, y, z)))
        _padding(bs)

        normals = []
        for i in range(myMesh["numVert"]):
            x = _getFloat(bs)
            y = _getFloat(bs)
            z = _getFloat(bs)
            normals.append(NoeVec3((x, y, z)))
        _padding(bs)

        if bs.readBytes(4)[3] == 0xFF:  # Skip, may be weights
            bs.seek(-4, NOESEEK_REL)
            bs.readBytes(myMesh["numVert"] * 4)
            _padding(bs)

        uvs = []
        for i in range(myMesh["numVert"]):
            u = _getFloat(bs)
            v = _getFloat(bs) 1.0
            uvs.append(NoeVec3((u, v, 0.0)))
        _padding(bs)

        if bs.tell() != myMesh["faceOffset"]:
            print("  SKIP: UV exceeds faceOffset: %d" % bs.tell())
            continue

        idxList = []
        for i in range(myMesh["numFace"]):
            indices = (bs.readUShort(), bs.readUShort(), bs.readUShort())
            idxList.append(indices[2])
            idxList.append(indices[1])
            idxList.append(indices[0])
        _padding(bs)

        meshName = "mesh_X" % myMesh["vertOffset"]
        mesh = NoeMesh(idxList, verts, meshName)
        mesh.setNormals(normals)
        mesh.setUVs(uvs)
        meshes.append(mesh)

    mdlList.append(NoeModel(meshes))
    return 1


def _getFloat(bs: NoeBitStream):
    """ get float value, skipping NaN
    """
    val = bs.readFloat()
    if not math.isnan(val):
        return val
    else:
        return bs.readFloat()


def _padding(bs: NoeBitStream):
    """ padding by 16 bytes
    """
    mod = bs.tell() % 16
    if mod != 0:
        bs.readBytes(16 - mod)


You will also need to use the following tools.

1. CPK Unpacker (to obtain fed files):
https://github.com/esperknight/CriPakTo ... ster/Build

2. QuickBMS and this script (to extract *.tex files):
http://aluigi.altervista.org/bms/fpactex.bms

3. Texture Unpacker (to convert textures):
https://github.com/xdanieldzd/GXTConvert/releases
  • Author
  • Localization

Tsuna__, posted Wed Jun 20, 2018 1:19 pm (36053)


It doesn't work.

i get this

"Successfully decompressed zRIF from provided license string.
failed to find unicv.db file or icv.db in folder"
  • Author
  • Localization

kana4567, posted Fri Jun 22, 2018 7:24 am (36107)


Here is a little guide to decrypt files. A retail game is needed.

Install h-encore on PS Vita 3.68.
https://github.com/TheOfficialFloW/h-encore

Install AntiBlacklist (in case of VitaTV).
https://vitadb.rinnegatamante.it/#/info/11

Install NoNpDrm and reboot device.
https://github.com/TheOfficialFloW/NoNpDrm#installation
https://github.com/TheOfficialFloW/NoNp ... ke-license

When you first launch the game, a file "6488b73b912a753a492e2714e9b38bc7.rif" will be created.
Open it by hex editor and you will see the license key at 0x50.

Copy "gro0:app/TITLE_ID" folder to your PC via FTP, then decrypt files using psvpfsparser.
Note that the license key corresponds "--klicensee" parameter.
https://github.com/motoharu-gosuto/psvpfstools/releases
  • Author
  • Localization

kana4567, posted Fri Jun 22, 2018 6:32 pm (36117)


beargood, congrats!

> it seems that some files are not expanded correctly

You don't have to worry about that.
Some head models contains strange triangle meshes between the face (top) and hairs (bottom).
Please delete those meshes at the middle, after exporting.

One more thing, the original filenames (*.fed, *.tex) are listed in the following files:
- TITLE_ID/media/afs/CharaModel.als
- TITLE_ID/media/afs/CharaTex.als
  • Author
  • Localization

Tsuna__, posted Fri Jun 22, 2018 7:54 pm (36119)


Ah, thats cool i hope it will work for me too once i get my 3.60 vita

thank you for the tutorial, finger crossed, as they say
  • Author
  • Localization

Tsuna__, posted Wed Jun 27, 2018 1:01 pm (36223)


Edit:
Well i did it, tho all the models seem to miss the ears, do you know by any chance where they are ?
  • Author
  • Localization

kana4567, posted Thu Jun 28, 2018 3:34 pm (36266)


> all the models seem to miss the ears

Wow, I never realized it, as most girls in this game have hairs which covers ears!
Unfortunately, my script can't load (find) those sub-meshes.

But I guess ears should exist in the same *.fed file, because the file has string like "EAR" or "EARShape".
  • Author
  • Localization

Tsuna__, posted Thu Jun 28, 2018 4:41 pm (36271)


Yeah also some of them miss some hair accessories like bows
  • Author
  • Localization

kana4567, posted Fri Jun 29, 2018 2:19 pm (36288)


Sorry but I don't have enough time and information (about file format) to fix that.
For now, I recommend you to create your own sub-meshes if there were some missing parts.

All I did is just finding chunks of floating-point numbers, by viewing file in Hex Workshop like this:


Anyway, If anyone knows similar 3D model format (by CRIWARE), please let me know!
I believe that *.fed files also contain bones, weights and blendshapes.
Guest
This topic is now closed to further replies.

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.