Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jobs:
ruff check
ruff format --check
pytest
python tools/verify_windower_abi.py
python -m compileall -q tools

clang-format:
Expand Down
38 changes: 38 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ set(FLUFFYTAIL_SOURCES
"src/exports.def"
"src/fluffytail.cpp"
"src/fluffytail.hpp"
"src/hook_utils.cpp"
"src/hook_utils.hpp"
"src/render_runtime.cpp"
"src/render_runtime.hpp"
"src/tail_policy.hpp"
"src/windower.cpp"
"src/windower.hpp"
)

add_library(${ASHITA_PROJECT_NAME} MODULE ${FLUFFYTAIL_SOURCES})
Expand All @@ -67,9 +74,40 @@ ashita_sdk_set_compiler_warnings_as_errors(${ASHITA_PROJECT_NAME})
ashita_sdk_set_linker_defaults(${ASHITA_PROJECT_NAME})
# Required for D3D device calls in the 32-bit Ashita host.
target_link_options(${ASHITA_PROJECT_NAME} PRIVATE /SAFESEH:NO)
if (MSVC)
# Host and COM callbacks use catch-all guards as their final fault boundary.
# /EHa is required for those guards to include Windows structured exceptions.
target_compile_options(${ASHITA_PROJECT_NAME} PRIVATE /EHa)
endif()

if (CMAKE_BUILD_TYPE STREQUAL "Debug")
ashita_sdk_set_linker_enable_debug_linking(${ASHITA_PROJECT_NAME})
else()
ashita_sdk_set_linker_disable_debug_linking(${ASHITA_PROJECT_NAME})
endif()

include(CTest)
if (BUILD_TESTING)
add_executable(fluffytail_native_tests
"tests/native_tests.cpp"
"src/hook_utils.cpp"
"src/hook_utils.hpp"
"src/tail_policy.hpp"
"src/windower.hpp"
)
target_include_directories(fluffytail_native_tests PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/src"
)
set_target_properties(fluffytail_native_tests PROPERTIES
CXX_EXTENSIONS ON
CXX_STANDARD_REQUIRED ON
)
ashita_sdk_attach(fluffytail_native_tests)
ashita_sdk_set_compiler_defaults(fluffytail_native_tests)
ashita_sdk_set_compiler_warn_level(fluffytail_native_tests 4)
ashita_sdk_set_compiler_warnings_as_errors(fluffytail_native_tests)
add_dependencies(fluffytail_native_tests ${ASHITA_PROJECT_NAME})
add_test(NAME fluffytail_native_tests
COMMAND fluffytail_native_tests "$<TARGET_FILE:${ASHITA_PROJECT_NAME}>"
)
endif()
8 changes: 5 additions & 3 deletions LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
| Component | Source and provenance | Affected files | License text |
| --- | --- | --- | --- |
| Ashita CMake and Direct3D scaffold | [AshitaXI/screenshot-src at `2d8812f`](https://github.com/AshitaXI/screenshot-src/tree/2d8812fd98f652f457e273cf74bad31a4bcc3ec9) | `CMakeLists.txt`, `cmake/FindAshitaSDK.cmake`, `src/defines.hpp` | [GNU GPL v3](LICENSE.GPL.txt) |
| Ashita plugin interface scaffold | [AshitaXI/exampleplugin at `b67726f`](https://github.com/AshitaXI/exampleplugin/tree/b67726f02fb55e6d2aef94a402ef6875384116eb) | `src/fluffytail.cpp`, `src/fluffytail.hpp`, `src/exports.def` | [GNU LGPL v3](LICENSE.md) |
| Ashita plugin interface scaffold | [AshitaXI/exampleplugin at `b67726f`](https://github.com/AshitaXI/exampleplugin/tree/b67726f02fb55e6d2aef94a402ef6875384116eb) | `src/fluffytail.cpp`, `src/fluffytail.hpp` | [GNU LGPL v3](LICENSE.md) |
| Ashita v4 SDK | Distributed separately with Ashita and selected by `ASHITA4_SDK_PATH` at build time | External headers and libraries, not included in this repository | [GNU LGPL v3](LICENSE.md) |
| FluffyTail C++ implementation | Modifications to the Ashita plugin scaffold authored in this repository | FluffyTail code in `src/fluffytail.cpp`, `src/fluffytail.hpp`, and `src/exports.def` | [GNU LGPL v3](LICENSE.md), as stated in the source headers |
| Release tooling and tests | Python tools and synthetic tests authored in this repository | `tools/`, `tests/`, and `pyproject.toml` | No license is currently stated for these files |
| Windower native plugin ABI declarations | [Shirk/Nameplate Windozer declarations](https://github.com/Shirk/Nameplate), including `Windozer.h` and `Windozer.cpp`; adapted for Windower 4.7.9 | `src/windower.hpp`, `src/windower.cpp` | [GNU GPL v3](LICENSE.GPL.txt), as stated in the source headers |
| Dual-host export definition | Ashita and Windower export names authored from their respective host contracts; the Windower entries derive from the GPLv3 Windozer declarations | `src/exports.def` | [GNU GPL v3](LICENSE.GPL.txt) |
| FluffyTail C++ implementation | Modifications to the Ashita plugin scaffold and shared runtime authored in this repository | `src/fluffytail.cpp`, `src/fluffytail.hpp`, `src/hook_utils.cpp`, `src/hook_utils.hpp`, `src/render_runtime.cpp`, `src/render_runtime.hpp`, and `src/tail_policy.hpp` | [GNU LGPL v3](LICENSE.md), as stated in the source headers |
| Release tooling and tests | Python tools and synthetic tests authored in this repository | `tools/`, `tests/`, and `pyproject.toml` | [GNU LGPL v3](LICENSE.md) |

The release DAT overlay contains modified Final Fantasy XI model data. Final Fantasy
XI and its models and textures are property of Square Enix. See the disclaimer in
Expand Down
148 changes: 78 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,103 +1,111 @@
<h1 align="center">FluffyTail</h1>
# FluffyTail

<p align="center">
Fluffy tails calibrated to each Mithra face and hair variant.<br>
An Ashita v4.3 plugin and XiPivot overlay for retail Final Fantasy XI.
</p>
FluffyTail adds a fluffy tail to the retail Mithra body armor model and selects
the tail colour for each face and hair variant. One x86 DLL contains adapters for
both supported hosts:

<p align="center">
<a href="LICENSE.GPL.txt"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPLv3"></a>
<a href="LICENSE.md"><img src="https://img.shields.io/badge/License-LGPLv3-blue.svg" alt="License: LGPLv3"></a>
<a href="https://github.com/Aeshur/FluffyTail/actions/workflows/quality.yml"><img src="https://github.com/Aeshur/FluffyTail/actions/workflows/quality.yml/badge.svg" alt="Checks"></a>
</p>
- Ashita v4.3 (the Ashita plugin ABI)
- Windower 4.7.9 (the native plugin ABI, interface version `0x04070300`)

## About

FluffyTail combines an Ashita v4.3 plugin with the bundled XiPivot `pivot`
polplugin. It matches each normal Mithra tail to the character's face and hair
variant. Supported NPCs with fixed models receive colours baked into their DATs.
The release also contains a 408-file XiPivot overlay. Fixed-model NPC colours
are baked into their DATs; normal Mithra colours are selected by the plugin at
render time.

## Install

Choose the archive for the host you use. Do not install both copies of the
overlay into one host.

### Ashita

Requirements:

- [Ashita v4.3](https://github.com/AshitaXI/Ashita-v4beta)
- The bundled `pivot` polplugin enabled in the active Ashita boot profile
- The XiPivot `pivot` polplugin, installed separately and enabled in the active
Ashita boot profile

1. Download the
1. Download `fluffytail.zip` from the
[latest release](https://github.com/Aeshur/FluffyTail/releases/latest/download/fluffytail.zip)
and extract it into the Ashita directory. Merge the included `plugins/` and
`polplugins/` folders with the existing folders.
2. Open the active profile in `Ashita/config/boot/` and ensure it contains:

```ini
[ashita.polplugins]
pivot = 1
```

3. Open `Ashita/config/pivot/pivot.ini` and add `FluffyTail` under `[overlays]`
using the next unused number. For example:

```ini
[overlays]
0=another-overlay
1=FluffyTail
```
and extract it into the Ashita directory. Merge `plugins/` and
`polplugins/` with the existing directories.
2. Add `FluffyTail` to the `[overlays]` section in your existing
`Ashita/config/pivot/pivot.ini`, using the next unused number. The package
does not include or replace this settings file or the XiPivot binary.
3. Ensure the active boot profile enables `pivot`, then load the plugin with
`/load fluffytail` (or add that command to the profile script).

4. Start FFXI and run `/load fluffytail`.
### Windower

To load the plugin automatically, add `/load fluffytail` to the script used by the
active Ashita boot profile.

When updating an existing installation, exit FFXI before replacing the DLL or DATs.

## Commands

`/fluffytail inspect` prints the current target's entity type, race, actor pointer,
and armor model IDs. It requires a current target and is intended for diagnosing
unsupported Mithra with fixed models.

## Troubleshooting
Requirements:

Keep the XiPivot overlay enabled while the plugin is loaded. With the plugin off,
normal Mithra tails use the neutral fallback colour while supported NPCs with fixed
models retain colours baked to match their hair.
- [Windower 4.7.9](https://windower.net/)
- The XiPivot `XIPivot` addon, installed separately

## Known limitations
1. Download `fluffytail-windower.zip` and extract it into the Windower
directory. Merge `plugins/` and `addons/` with the existing directories.
2. Add the `FluffyTail` overlay to your existing XIPivot configuration. The
package writes only `addons/XIPivot/data/DATs/FluffyTail/ROM/`; it never
overwrites XIPivot settings or bundles the XIPivot addon.
3. Load the native plugin with Windower's plugin loader (`//load fluffytail`).

- Costume bodies that hide the original tail remain unchanged.
- Nanaa Mihgo's special fixed model uses an unsupported tail-body rig and is not
modified.
Exit the game before replacing the DLL or DAT files. Keep XiPivot enabled while
the plugin is loaded. Costume bodies that hide the original tail remain
unchanged, and Nanaa Mihgo's unsupported fixed-model rig is not modified.

## Build

The build requires Ninja, CMake 3.22 or later, the Ashita v4 SDK, and the x86
MSVC build environment. Set `ASHITA4_SDK_PATH` to the SDK directory:
The build requires CMake 3.22 or later, Ninja, an x86 MSVC Native Tools prompt,
the Ashita v4 SDK, and the private DAT rig assets used by the overlay tools. Set
`ASHITA4_SDK_PATH` to the SDK directory:

```batch
set "ASHITA4_SDK_PATH=C:\Games\YourAshita\plugins\sdk"
cmake --preset x86-release-win
cmake --build --preset x86-release-win
```

From an x86 Native Tools Command Prompt, run:
The result is the single dual-host `bin/fluffytail.dll`. Build the neutral
runtime overlay and both deterministic release archives with:

```batch
cmake --preset x86-release-win
cmake --build --preset x86-release-win
python tools/build_runtime_overlay.py
python tools/build_packages.py bin/fluffytail.dll work/runtime-overlay work/packages
```

A successful configure and build writes `bin/fluffytail.dll`.
This writes `work/packages/fluffytail.zip` for Ashita and
`work/packages/fluffytail-windower.zip` for Windower. Existing output files
require `--force` or a new output directory. Each archive contains exactly 413
entries: the shared DLL, 408 validated DAT files, the README and provenance
inventory, and the GPLv3 and LGPLv3 notices under `FluffyTail/`.

## License
Run the offline checks before packaging:

<a href="LICENSE.GPL.txt"><img src="https://www.gnu.org/graphics/gplv3-127x51.png" alt="GNU GPLv3 logo"></a>
<a href="LICENSE.md"><img src="https://www.gnu.org/graphics/lgplv3-147x51.png" alt="GNU LGPLv3 logo"></a>
```batch
python -m pytest
ruff check tools tests
python tools/verify_windower_abi.py
```

## Validation status

The Ashita CMake and Direct3D scaffold uses
[GNU GPL version 3](LICENSE.GPL.txt). The plugin interface scaffold and original
C++ work created by this project use [GNU LGPL version 3](LICENSE.md). The
[component license inventory](LICENSES.md) records exact provenance and affected
files. No license is currently stated for the Python release tools and tests.
Validation is offline only. The test suite checks DAT chunk structure, overlay
file counts and paths, deterministic ZIP metadata, the x86 PE header, and the
Windower 4.7.9 ABI (`0x04070300`) against pinned official Hook and Config binary
hashes. No in-game rendering or host loader validation has been performed for
this port.

## License

*Final Fantasy XI* and its models and textures are property of Square Enix.
This project is an unofficial fan mod. It is unaffiliated with and unendorsed by
The Ashita CMake and Direct3D scaffold is [GNU GPL v3](LICENSE.GPL.txt). The
Ashita plugin interface scaffold and most FluffyTail C++ work are [GNU LGPL
v3](LICENSE.md). The Windower adapter and dual-host export definition include
GPLv3-derived Nameplate/Windozer material and are covered by [GNU GPL v3](LICENSE.GPL.txt).
The component inventory in [LICENSES.md](LICENSES.md) records exact provenance.
The Python release tools and tests are covered by [GNU LGPL v3](LICENSE.md).
The complete corresponding source for each published DLL is available from the
[FluffyTail source repository](https://github.com/Aeshur/FluffyTail) at the matching
release tag.

*Final Fantasy XI* and its models and textures are property of Square Enix. This
project is an unofficial fan mod and is unaffiliated with or unendorsed by
Square Enix.
6 changes: 5 additions & 1 deletion src/exports.def
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ EXPORTS
; Ashita v4 plugin ABI exports. Names and ordinals are part of the host contract.

expCreatePlugin @1

expDestroyPlugin @2
expGetInterfaceVersion @3

; Windower 4.7.9 native plugin ABI exports. These use C linkage and cdecl.

CreateInstance
GetInterfaceVersion
Loading