ff9Save

package module
v0.0.0-...-b65ab96 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 12, 2023 License: GPL-3.0 Imports: 16 Imported by: 0

README ΒΆ

Final Fantasy IX Save Converter

build codecov Bugs Code Smells GoDoc

Description πŸ“–

Full implementation of the 2016 FF9 save system. Allows you to read, write, and edit any 2016 save.

This library also has a command line utility if you just want to convert your saves between json and binary format.

Supported Formats
  • Encrypted Save file used in the PC version
  • Json save file used in the Switch version
Unsupported Formats
  • PS1 save file
  • original PC save file

Warning ⚠️

Back up your save files before using this converter. I can't be 100% certain all save files will convert properly.

Converting to switch saves is lossy. Some information is not carried over to the switch save files since it doesn't include those fields.

Here are the list of fields not carried over:

  • Auto Login
  • System Achievement Statuses
  • Screen Rotation

The command line utility will initialize these fields with default values.

Bug Reporting πŸ›

If this tool generates a corrupted save file please report it. Please submit an issue on github including:

  • What is corrupted in the game
  • Attaching both save files original and generated
  • What direction you where converting switch to pc or pc to switch

Command line Utility ️πŸ–₯️

After downloading the application you will need to run it from the command prompt or terminal depending on your OS.

To use this application will ether need the PC save file located at

AppData\LocalLow\SquareEnix\FINAL FANTASY IX\Steam\EncryptedSavedData\SavedData_ww.dat

or a switch save which can be dumped using homebrew like checkpoint.

Final Fantasy 9 Save Converter
Usage:
        ff9sc FILE DIRECTORY
        ff9sc DIRECTORY FILE
Examples:
        $ ff9sc SavedData_ww.dat switchSavesFolder
        $ ff9sc switchSavesFolder SavedData_ww.dat

The first file path in the command is what you're converting from the second is what your converting too

Download πŸ’½

Click the download link and select the version for your operating system

Download

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

View Source
const AES256KeySize = 32
View Source
const FilePreviewReservedSize = 1024
View Source
const FilePreviewSize = 965
View Source
const FileReservedSize = 18432
View Source
const FileSize = 17970
View Source
const FilesPerSlot = 15
View Source
const MaxSlots = 10
View Source
const MetaDataReservedSize = 320
View Source
const MetaDataSize = 288 //this is including header

Variables ΒΆ

View Source
var NoneHeader = [4]byte{'N', 'O', 'N', 'E'}
View Source
var PrevHeader = [4]byte{'P', 'R', 'E', 'V'}
View Source
var SaveHeader = [4]byte{'S', 'A', 'V', 'E'}

Functions ΒΆ

func Decrypt ΒΆ

func Decrypt(data []byte) ([]byte, error)

Decrypt with AES CBC mode using the ff9 password and salt removing pkcs7 padding

func DecryptAndReadSaveSection ΒΆ

func DecryptAndReadSaveSection(buf *bytes.Buffer, size, reservedSize int) ([]byte, error)

func Encrypt ΒΆ

func Encrypt(data []byte) ([]byte, error)

Encrypt with AES CBC mode using the ff9 password and salt including pkcs7 padding

func EncryptAndWriteSaveSection ΒΆ

func EncryptAndWriteSaveSection(buf *bytes.Buffer, data []byte, reservedSize int) error

func GenerateSaveFileName ΒΆ

func GenerateSaveFileName(prefix string, filePos int) string

func GetFilePos ΒΆ

func GetFilePos(prefix string, fileName string) int

func GetSlotAndFileNumber ΒΆ

func GetSlotAndFileNumber(filePos int) (int, int)

func LanguageIntToLanguageString ΒΆ

func LanguageIntToLanguageString() map[int32]string

func LanguageStringToLanguageInt ΒΆ

func LanguageStringToLanguageInt() map[string]int32

Types ΒΆ

type Achievement_80000 ΒΆ

type Achievement_80000 struct {
	AteCheckArray    [100]int32FromStr `json:"AteCheckArray,string"`
	EvtReservedArray [17]int32FromStr  `json:"EvtReservedArray,string"`
	BlkMag_no        int32             `json:"blkMag_no,string"`
	WhtMag_no        int32             `json:"whtMag_no,string"`
	BluMag_no        int32             `json:"bluMag_no,string"`
	Summon_no        int32             `json:"summon_no,string"`
	Enemy_no         int32             `json:"enemy_no,string"`
	BackAtk_no       int32             `json:"backAtk_no,string"`
	Defence_no       int32             `json:"defence_no,string"`
	Trance_no        int32             `json:"trance_no,string"`
	Abilities        [221]int32FromStr `json:"abilities,string"`
	PassiveAbilities [63]int32FromStr  `json:"passiveAbilities,string"`
	SynthesisCount   int32             `json:"synthesisCount,string"`
	AuctionTime      int32             `json:"AuctionTime,string"`
	StiltzkinBuy     int32             `json:"StiltzkinBuy,string"`
	QuadmistWinList  [300]int32FromStr `json:"QuadmistWinList,string"`
}

type Achievement_98000 ΒΆ

type Achievement_98000 struct {
	Abnormal_status_00001            uint32            `json:"00001_abnormal_status,string"`
	Summon_shiva_00002               boolFromStr       `json:"00002_summon_shiva"`
	Summon_ifrit_00003               boolFromStr       `json:"00003_summon_ifrit"`
	Summon_ramuh_00004               boolFromStr       `json:"00004_summon_ramuh"`
	Summon_carbuncle_reflector_00005 boolFromStr       `json:"00005_summon_carbuncle_reflector"`
	Summon_carbuncle_haste_00006     boolFromStr       `json:"00006_summon_carbuncle_haste"`
	Summon_carbuncle_protect_00007   boolFromStr       `json:"00007_summon_carbuncle_protect"`
	Summon_carbuncle_shell_00008     boolFromStr       `json:"00008_summon_carbuncle_shell"`
	Summon_fenrir_earth_00009        boolFromStr       `json:"00009_summon_fenrir_earth"`
	Summon_fenrir_wind_000010        boolFromStr       `json:"000010_summon_fenrir_wind"`
	Summon_atomos_000011             boolFromStr       `json:"000011_summon_atomos"`
	Summon_phoenix_000012            boolFromStr       `json:"000012_summon_phoenix"`
	Summon_leviathan_000013          boolFromStr       `json:"000013_summon_leviathan"`
	Summon_odin_000014               boolFromStr       `json:"000014_summon_odin"`
	Summon_madeen_000015             boolFromStr       `json:"000015_summon_madeen"`
	Summon_bahamut_000016            boolFromStr       `json:"000016_summon_bahamut"`
	Summon_arc_000017                boolFromStr       `json:"000017_summon_arc"`
	ReservedBuffer_99999             [123]int32FromStr `json:"99999_ReservedBuffer,string"`
}

type CharacterInfo ΒΆ

type CharacterInfo struct {
	SerialID int32
	Level    int32
	Name     String128
}

type Common_40000 ΒΆ

type Common_40000 struct {
	Player      [9]Player       `json:"players,string"`
	Slot        [4]byteFromStr  `json:"slot,string"`
	Escape_no   uint16          `json:"escape_no,string"`
	Summon_flag uint16          `json:"summon_flag,string"`
	Gil         uint32          `json:"gil,string"`
	Frog_no     int16           `json:"frog_no,string"`
	Steal_no    int16           `json:"steal_no,string"`
	Dragon_no   int16           `json:"dragon_no,string"`
	Items       [256]Item       `json:"items,string"`
	Rareitems   [64]byteFromStr `json:"rareItems,string"`
}

type Common_94000 ΒΆ

type Common_94000 struct {
	Player_bonus_00001   [9]uint32FromStr  `json:"00001_player_bonus,string"`
	ReservedBuffer_99999 [119]int32FromStr `json:"99999_ReservedBuffer,string"`
}

type Event_20000 ΒΆ

type Event_20000 struct {
	GStepCount   int32    `json:"gStepCount,string"`
	GEventGlobal String4K `json:"gEventGlobal"` //size of large string //this could also be rune
}

type File ΒΆ

type File struct {
	Data struct {
		State_10000       State_10000       `json:"10000_State"`
		Event_20000       Event_20000       `json:"20000_Event"`
		MiniGame_30000    MiniGame_30000    `json:"30000_MiniGame"`
		Common_40000      Common_40000      `json:"40000_Common"`
		Setting_50000     Setting_50000     `json:"50000_Setting"`
		Sound_60000       Sound_60000       `json:"60000_Sound"`
		World_70000       World_70000       `json:"70000_World"`
		Achievement_80000 Achievement_80000 `json:"80000_Achievement"`
		State_91000       [128]int32FromStr `json:"91000_State"`
		Event_92000       [128]int32FromStr `json:"92000_Event"`
		MiniGame_93000    [128]int32FromStr `json:"93000_MiniGame"`
		Common_94000      Common_94000      `json:"94000_Common"`
		Setting_95000     Setting_95000     `json:"95000_Setting"`
		Sound_96000       [128]int32FromStr `json:"96000_Sound"`
		World_97000       [128]int32FromStr `json:"97000_World"`
		Achievement_98000 Achievement_98000 `json:"98000_Achievement"`
		Other_99000       [384]int32FromStr `json:"99000_Other"`
	}
}

func (*File) BinaryMarshaler ΒΆ

func (f *File) BinaryMarshaler() ([]byte, error)

func (*File) BinaryUnmarshaler ΒΆ

func (f *File) BinaryUnmarshaler(data []byte) error

type FileInfo ΒΆ

type FileInfo struct {
	LatestSlot      int32
	LatestSave      int32
	LatestTimestamp float64
}

type FilePreview ΒΆ

type FilePreview struct {
	IsPreviewCorrupted bool
	HasData            bool
	Gil                int64
	PlayDuration       uint64
	WinType            uint64 `json:"win_type"`
	Location           String128
	CharacterInfoList  [4]CharacterInfo
	Timestamp          float64
	ReservedData       [64]int32
}

func (*FilePreview) BinaryMarshaler ΒΆ

func (fp *FilePreview) BinaryMarshaler() ([]byte, error)

func (*FilePreview) BinaryUnmarshaler ΒΆ

func (fp *FilePreview) BinaryUnmarshaler(data []byte) error

func (*FilePreview) FixCharacterInfoForBinary ΒΆ

func (fp *FilePreview) FixCharacterInfoForBinary()

in json the values are 0 for empty characterInfo but in binary they are -1

func (*FilePreview) FixCharacterInfoFromBinary ΒΆ

func (fp *FilePreview) FixCharacterInfoFromBinary()

type Item ΒΆ

type Item struct {
	ID    byte `json:"id,string"`
	Count byte `json:"count,string"`
}

type MetaData ΒΆ

type MetaData struct {
	SaveVersion               float32 //1f
	DataSize                  int32   //they always plus 4 onto this to account for the NONE or SAVE Header rembere to remove 4 bytes from my file size count
	FileInfo                  FileInfo
	IsGameFinishFlag          int32
	SelectedLanguage          int32 //PrefsLanguage //will have to figure out how to use the enum convertion on binary unmarshal
	IsAutoLogin               int8
	SystemAchievementStatuses byte //this is an array but don't think it matters its a size of 1
	ScreenRotation            byte
	ReservedBuffer            [249]byte
}

func NewMetaData ΒΆ

func NewMetaData() MetaData

func (*MetaData) BinaryMarshaler ΒΆ

func (md *MetaData) BinaryMarshaler() ([]byte, error)

type MiniGameCard ΒΆ

type MiniGameCard struct {
	ID     byte  `json:"id,string"`
	Side   byte  `json:"side,string"`
	Atk    byte  `json:"atk,string"`
	Type   int32 `json:"type,string"`
	Pdef   byte  `json:"pdef,string"`
	Mdef   byte  `json:"mdef,string"`
	Cpoint byte  `json:"cpoint,string"`
	Arrow  byte  `json:"arrow,string"`
}

type MiniGame_30000 ΒΆ

type MiniGame_30000 struct {
	SWin         int16             `json:"sWin,string"`
	SLose        int16             `json:"sLose,string"`
	SDraw        int16             `json:"sDraw,string"`
	MiniGameCard [100]MiniGameCard `json:"MiniGameCard,string"`
}

type Player ΒΆ

type Player struct {
	Name     String128 `json:"name,string"` //assuming 128 could be wrong
	Category byte      `json:"category,string"`
	Level    byte      `json:"level,string"`
	Exp      uint32    `json:"exp,string"`
	Cur      struct {
		Hp      uint16 `json:"hp,string"`
		Mp      int16  `json:"mp,string"`
		At      int16  `json:"at,string"`
		At_coef int8   `json:"at_coef,string"` //sbyte
		Capa    byte   `json:"capa,string"`
	} `json:"cur"`
	Max struct {
		Hp      uint16 `json:"hp,string"`
		Mp      int16  `json:"mp,string"`
		At      int16  `json:"at,string"`
		At_coef int8   `json:"at_coef,string"` //sbyte
		Capa    byte   `json:"capa,string"`
	} `json:"max"`
	Trance   byte `json:"trance,string"`
	Web_bone byte `json:"web_bone,string"` //should this always be 0?
	Elem     struct {
		Dex byte `json:"dex,string"`
		Str byte `json:"str,string"`
		Mgc byte `json:"mgc,string"`
		Wpr byte `json:"wpr,string"`
	} `json:"elem"`
	Defence struct {
		P_def byte `json:"p_def,string"`
		P_ev  byte `json:"p_ev,string"`
		M_def byte `json:"m_def,string"`
		M_ev  byte `json:"m_ev,string"`
	} `json:"defence"`
	Basis struct {
		Max_hp int16 `json:"max_hp,string"`
		Max_mp int16 `json:"max_mp,string"`
		Dex    byte  `json:"dex,string"`
		Str    byte  `json:"str,string"`
		Mgc    byte  `json:"mgc,string"`
		Wpr    byte  `json:"wpr,string"`
	} `json:"basis"`
	Info struct {
		Slot_no   byte `json:"slot_no,string"`
		Serial_no byte `json:"serial_no,string"`
		Row       byte `json:"row,string"`
		Win_pose  byte `json:"win_pose,string"`
		Party     byte `json:"party,string"`
		Menu_type byte `json:"menu_type,string"`
	} `json:"info"`
	Status byte           `json:"status,string"`
	Equip  [5]byteFromStr `json:"equip,string"`
	Bonus  struct {
		Dex uint16 `json:"dex,string"`
		Str uint16 `json:"str,string"`
		Mgc uint16 `json:"mgc,string"`
		Wpr uint16 `json:"wpr,string"`
	} `json:"bonus"`
	Pa [48]byteFromStr  `json:"pa,string"`
	Sa [2]uint32FromStr `json:"sa,string"`
}

type PrefsLanguage ΒΆ

type PrefsLanguage struct {
	Value string //find out type //int32 on marshal
}

possibly enum this with all possible languages

type SavedData ΒΆ

type SavedData struct {
	MetaData     MetaData
	FilePreviews [150]FilePreview
	Auto         File
	Slot         [150]File
}

func NewSavedData ΒΆ

func NewSavedData() SavedData

func (*SavedData) BinaryMarshaler ΒΆ

func (sd *SavedData) BinaryMarshaler() ([]byte, error)

func (*SavedData) BinaryUnmarshaler ΒΆ

func (sd *SavedData) BinaryUnmarshaler(data []byte) error

type Setting_50000 ΒΆ

type Setting_50000 struct {
	Cfg struct {
		Sound                 uint64           `json:"sound,string"`
		Sound_effect          uint64           `json:"sound_effect,string"`
		Control               uint64           `json:"control,string"`
		Cursor                uint64           `json:"cursor,string"`
		Atb                   uint64           `json:"atb,string"`
		Camera                uint64           `json:"camera,string"`
		Move                  uint64           `json:"move,string"`
		Vibe                  uint64           `json:"vibe,string"`
		Btl_speed             uint64           `json:"btl_speed,string"`
		Fld_msg               uint64           `json:"fld_msg,string"`
		Here_icon             uint64           `json:"here_icon,string"`
		Win_type              uint64           `json:"win_type,string"`
		Target_win            uint64           `json:"target_win,string"`
		Control_data          uint64           `json:"control_data,string"`
		Control_data_keyboard [10]int32FromStr `json:"control_data_keyboard,string"` //most likely int32 but not sure
		Control_data_joystick [10]String128    `json:"control_data_joystick,string"` //assuming 128
		Skip_btl_camera       uint64           `json:"skip_btl_camera,string"`
	} `json:"cfg"`
	Time float32 `json:"time,string"`
}

type Setting_95000 ΒΆ

type Setting_95000 struct {
	Time_00001           float64           `json:"00001_time,string"`
	ReservedBuffer_99999 [126]int32FromStr `json:"99999_ReservedBuffer,string"`
}

type Sound_60000 ΒΆ

type Sound_60000 struct {
	Auto_save_bgm_id int32 `json:"auto_save_bgm_id,string"`
}

type State_10000 ΒΆ

type State_10000 struct {
	Mode         byte        `json:"mode,string"`
	PrevMode     byte        `json:"prevMode,string"`
	FldMapNo     int16       `json:"fldMapNo,string"`
	FldLocNo     int16       `json:"fldLocNo,string"`
	BtlMapNo     int16       `json:"btlMapNo,string"`
	BtlSubMapNo  int8        `json:"btlSubMapNo,string"` //sbyte
	WldMapNo     int16       `json:"wldMapNo,string"`
	WldLocNo     int16       `json:"wldLocNo,string"`
	TimeCounter  float32     `json:"timeCounter,string"` //math.float32bits might be needed don't think its encoding with the right standard
	TimerControl boolFromStr `json:"timerControl"`
	TimerDisplay boolFromStr `json:"timerDisplay"`
}

type String128 ΒΆ

type String128 [128]byte

func (String128) MarshalJSON ΒΆ

func (s String128) MarshalJSON() ([]byte, error)

func (*String128) UnmarshalJSON ΒΆ

func (s *String128) UnmarshalJSON(data []byte) error

type String4K ΒΆ

type String4K [4096]byte

func (String4K) MarshalJSON ΒΆ

func (s String4K) MarshalJSON() ([]byte, error)

func (*String4K) UnmarshalJSON ΒΆ

func (s *String4K) UnmarshalJSON(data []byte) error

type World_70000 ΒΆ

type World_70000 struct {
	DataCameraStateRotationMax       float32     `json:"data.cameraState.rotationMax,string"`
	DataCameraStateUpperCounter      int16       `json:"data.cameraState.upperCounter,string"`
	DataCameraStateUpperCounterSpeed int32       `json:"data.cameraState.upperCounterSpeed,string"`
	DataCameraStateUpperCounterForce boolFromStr `json:"data.cameraState.upperCounterForce"`
	DataCameraStateRotation          float32     `json:"data.cameraState.rotation,string"`
	DataCameraStateRotationRev       float32     `json:"data.cameraState.rotationRev,string"`
	DataHintmap                      uint32      `json:"data.hintmap,string"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL