1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-29 07:37:45 +00:00
Conflicts:
	CMakeLists.txt
	src/servers/Server_Common/Network/PacketDef/Ipcs.h
	src/servers/Server_Zone/Actor/PlayerQuest.cpp
	src/servers/Server_Zone/Actor/PlayerSql.cpp
This commit is contained in:
Mordred Admin 2017-10-25 14:49:35 +02:00
commit 2113e45a5b
69 changed files with 1794 additions and 383 deletions

8
.gitignore vendored
View file

@ -44,6 +44,10 @@ build/
*.out
*.app
# Projects and Solutions
*.vcxproj
*.sln
# CMake
CMakeCache.txt
CMakeFiles
@ -96,6 +100,10 @@ src/libraries/external/boost_*
*.ipdb
*.iobj
*.filters
*.idb
# sapphire version
src/servers/Server_Common/Version\.cpp
# edit and continue files
/enc_temp_folder

View file

@ -6,17 +6,19 @@ before_install:
- sudo apt-get update
- sudo apt-get install -y software-properties-common
- sudo apt-get update
- sudo apt-get install gcc-4.9 g++-4.9 gcc-4.9-multilib g++-4.9-multilib cmake3 -y
- sudo apt-get install gcc-7 g++-7 gcc-7-multilib g++-7-multilib cmake3 -y
- sudo apt-get install libboost-dev libboost-all-dev libmysqlclient-dev -y
- sudo apt-get install libmysqlcppconn-dev -y
compiler:
- gcc
- g++
# Build steps
script:
- g++ --version
- mkdir build
- cd build
- cmake .. -DSAPPHIRE_BOOST_VER="1.54.0" -DCMAKE_CXX_COMPILER=g++-4.9 -DCMAKE_C_COMPILER=gcc-4.9 && make -j 3
- cmake .. -DSAPPHIRE_BOOST_VER="1.54.0" -DCMAKE_CXX_COMPILER=g++-7 && make -j 3
- cd ..
- bash sql_import.sh

View file

@ -65,4 +65,4 @@ add_subdirectory("src/tools/exd_common_gen")
add_subdirectory("src/tools/exd_struct_gen")
add_subdirectory("src/tools/exd_struct_test")
add_subdirectory("src/tools/quest_parser")
add_subdirectory("src/tools/pcb_reader")

View file

@ -25,6 +25,6 @@
<MotD>&lt;&lt;&lt;Welcome to Sapphire&gt;&gt;&gt;</MotD>
<MotD>This is a very good server</MotD>
<MotD>You can change these messages by editing MotDArray in config/settings_zone.xml</MotD>
</MotdArray>
</MotDArray>
</Parameters>
</Settings>

View file

@ -1,18 +1,21 @@
body{
font-family:Verdana;
font-size:10pt;
line-height:14pt;
height:100%;
background-image:url(../../assets/img/background.png);
background-color:#282828;
html {
min-height: 100%;
}
div.contentContainer{
body {
font-family: Meiryo, Verdana, sans-serif;
font-size: 12px;
line-height: 14px;
height: 100%;
overflow: hidden;
}
.contentContainer{
width:50%;
float:left;
}
div.info{
.info{
width:320px;
height:100%;
margin-left:auto;
@ -22,7 +25,7 @@ div.info{
background-color:lightgrey;
}
div.infoFooter{
.infoFooter{
width:400px;
margin-left:auto;
margin-right:auto;
@ -135,3 +138,26 @@ p.pageSubTitle{
margin:0 auto;
}
.commit-history {
padding-left: 20px;
}
.commit-history li a {
color: #fff;
}
.commit-history li a:hover {
color: #bbb;
}
.s-left-half {
overflow-y: auto;
}
.s-link-badge {
font-size: 15px;
}
h2 {
font-size: 19px;
}

View file

@ -1,5 +1,138 @@
div#TopDiv{
padding:10px;
.s-logo {
padding: 25px 65px 15px 65px;
}
.s-logo > img {
width: 100%;
}
/*
Consider using vh?
*/
.s-left-half {
left: 0;
background: #232326;
/* box-shadow: inset -4px 0px 4px -3px #1a1a1ab3; */
color: white;
}
.s-right-half {
right: 0;
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00233B', endColorstr='#090915',GradientType=0 );
background: linear-gradient(#00233B 0%, #090915 100%);
border-left: 6px solid #16161b;
padding: 0;
}
.s-full-split {
position: absolute;
top: 0;
bottom: 0;
float: left;
z-index: 10;
}
.s-login-box {
background: #16161b;
position: relative;
z-index: 15;
padding: 0;
overflow: hidden;
width: 100%;
float: none;
}
.s-login-box-form {
padding: 0 35px 8px 25px;
overflow: hidden;
}
.s-login-input-wrapper {
color: white;
display: block;
width: 100%;
margin-bottom: 15px;
font-weight: normal;
font-size: 14px;
color: #fff;
}
.s-login-input-wrapper > input {
background: #444;
border: 0;
border-bottom: 3px solid #595959;
padding: 7px 0 7px 7px;
width: 100%;
margin-top: 8px;
font-size: 13.5px;
color: white;
}
.s-login-input-wrapper > input:focus {
border-color: #1868C2;
}
.s-login-input-wrapper > input:active {
border-color: #1868C2;
}
.s-login-input-wrapper > input:hover {
border-color: #1868C2;
}
.btn.btn-default {
border: 0;
color: white;
border-radius: 0;
font-weight: normal;
text-shadow: none;
box-shadow: none;
border-color: #1868C2;
color: white !important;
}
.s-login-btn {
width: 150px;
background: #0e6bc9;
right: -7px; /* ugh. refer to padding-left on input above - more bounding box issues... */
position: relative;
}
.s-login-btn:hover {
width: 150px;
background: #3593f2;
}
.s-acc-btn {
width: 100%;
background: #3B3B3B;
padding: 5px 0;
margin-top: 4px;
}
.s-acc-btn:hover {
background: #6b6b6b;
}
.s-error-text {
color: #ff3333;
margin: 3px;
}
.s-gears {
position: absolute;
right: 0;
bottom: 0;
width: 35%;
max-width: 350px;
overflow: hidden;
}
.s-gears > img {
width: 100%;
position: relative;
bottom: -40px;
}
img{
@ -12,8 +145,39 @@ h2.text-center{
color:#fff;
}
h1.text-center{
color:#FFF;
.s-login-call{
color: #fff;
margin-top: 15px;
font-size: 20px;
}
.s-link-badge-wrapper {
clear: both;
}
.s-link-badge {
background: #fff;
line-height: 48px;
text-indent: 55px;
display: inline-block;
padding: 0;
font-size: 17px;
margin-top: 25px;
color: white;
border-bottom: 3px solid #43348D;
margin-bottom: 15px;
}
.s-link-badge-discord {
background: #7082E1 url("../img/fa-discord-icon.png") 10px 7px no-repeat;
margin-right: 60px; /* Ideally we'd just use pull-right with the last col-xs-5 but IE7 bounding box render??? */
}
.s-link-badge-github {
background: #fff url("../img/github-icon.png") 10px 7px no-repeat;
color: #333;
border-bottom-color: #808080;
}
div.login-card{
@ -30,11 +194,7 @@ input.input-sm{
margin-right:auto;
}
div#Conttwo.container{
display:block;
width:300px;
background-color:rgba(17, 17, 17, 0.77);
}
div#Split{
padding:2px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View file

@ -23,7 +23,7 @@
var parsed = JSON.parse(response);
window.external.Boot(parsed.sId, parsed.lobbyHost, parsed.frontierHost);
}catch(err){
document.getElementById("Error").innerHTML = "Create User failed.";
document.getElementById("Error").innerHTML = "User creation failed.";
}
}
}
@ -46,25 +46,55 @@
</head>
<body scroll="no">
<div id="TopDiv"></div>
<div class="container"><img src="assets/img/sapphire_logo.png" width="40%" height="40%"></div>
<div class="container" id="Conttwo">
<div></div>
<h1 class="text-center">Create Account</h1>
<p class="text-center">Username: </p>
<td><input type="text" name="username" /></td>
<div id="Split"></div>
<p class="text-center">Password: </p>
<td><input type="password" name="password" /></td>
<div id="space"></div>
<button class="btn btn-default" input id="submitButton" onclick="doLogin()">Create User</button>
<p id="Error" class="text-center"></p>
<div id="Split">
<p id="CreateUser"><a href="login.html"><span style="text-decoration: underline;">Back to Login</span></a></p>
</div>
</div>
<div class="s-full-split s-left-half col-xs-7">
<div class="clearfix s-link-badge-wrapper">
<a href="https://discord.gg/KfrZCkx" class="s-link-badge s-link-badge-discord col-xs-5">
Join us on Discord!
</a>
<a href="https://github.com/SapphireMordred/Sapphire" class="s-link-badge s-link-badge-github col-xs-5 pull-left">
Our Github repository
</a>
</div>
<h2>Github stuff here soon!</h2>
</div>
<div class="s-full-split s-right-half col-xs-5">
<div class="s-logo">
<img src="assets/img/sapphire_logo_resize.png">
</div>
<div class="s-login-box col-xs-12">
<div class="s-login-box-form">
<h1 class="text-center s-login-call">Account Creation</h1>
<div class="input-control">
<label class="s-login-input-wrapper">
Username
<input type="text" name="username" autofocus>
</label>
</div>
<div class="input-control">
<label class="s-login-input-wrapper">
Password
<input type="password" name="password">
</label>
</div>
<button class="btn btn-default s-login-btn pull-right" id="CreateUser" onclick="doLogin()">Create Account</button>
<p id="Error" class="s-error-text"></p>
</div>
<a class="btn btn-default s-acc-btn" href="login.html">Back to Login</a>
</div>
<div class="s-gears">
<img class="s-gears" src="assets/img/graffletopia-gears.png" alt="">
</div>
</div>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script> // Run only after jq and bootstrap have finished loading
// Work around non-HTML5 compliant IE autofocus
$(function() {
$('[autofocus]:not(:focus)').eq(0).focus();
});
</script>
</body>
</html>

View file

@ -2,13 +2,13 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sapphire login</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/styles.css">
<link rel="stylesheet" href="assets/css/global.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sapphire - Login</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/styles.css">
<link rel="stylesheet" href="assets/css/global.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<script>
function doLogin(){
var url = "sapphire-api/lobby/login";
@ -42,29 +42,80 @@
}
return data;
}
</script>
</head>
<body scroll="no">
<div id="TopDiv"></div>
<div class="container"><img src="assets/img/sapphire_logo.png" width="40%" height="40%"></div>
<div class="container" id="Conttwo">
<div></div>
<h1 class="text-center">Login To Account</h1>
<p class="text-center">Username: </p>
<td><input type="text" name="username" /></td>
<div id="Split"></div>
<p class="text-center">Password: </p>
<td><input type="password" name="password" /></td>
<div id="space"></div>
<button class="btn btn-default" input id="submitButton" onclick="doLogin()">Login</button>
<p id="Error" class="text-center"></p>
<div id="Split">
<p id="CreateUser"><a href="createUser.html"><span style="text-decoration: underline;">Create Account</span></a></p>
</div>
</div>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<div class="s-full-split s-left-half col-xs-7">
<div class="clearfix s-link-badge-wrapper">
<a href="https://discord.gg/KfrZCkx" class="s-link-badge s-link-badge-discord col-xs-5">
Join us on Discord!
</a>
<a href="https://github.com/SapphireMordred/Sapphire" class="s-link-badge s-link-badge-github col-xs-5 pull-left">
Our Github repository
</a>
</div>
<h2>Recent Activity</h2>
<ul id="commit-log" class="commit-history">
</ul>
</div>
<div class="s-full-split s-right-half col-xs-5">
<div class="s-logo">
<img src="assets/img/sapphire_logo_resize.png">
</div>
<div class="s-login-box col-xs-12">
<div class="s-login-box-form">
<h1 class="text-center s-login-call">Login to Account</h1>
<div class="input-control">
<label class="s-login-input-wrapper">
Username
<input type="text" name="username" autofocus>
</label>
</div>
<div class="input-control">
<label class="s-login-input-wrapper">
Password
<input type="password" name="password">
</label>
</div>
<button class="btn btn-default s-login-btn pull-right" id="submitButton" onclick="doLogin()">Login</button>
<p id="Error" class="s-error-text"></p>
</div>
<a class="btn btn-default s-acc-btn" href="createUser.html">Create Account</a>
</div>
<div class="s-gears">
<img class="s-gears" src="assets/img/graffletopia-gears.png" alt="">
</div>
</div>
<div id="TopDiv"></div>
<div class="container"></div>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script>
fetch( "https://api.github.com/repos/SapphireMordred/Sapphire/events" ).then( function( resp ) {
return resp.json();
}).then( function( data ) {
data = data.filter( function( e ) {
return e.type == "PushEvent";
});
let commitContainer = document.getElementById( 'commit-log' );
for ( var evnt of data ) {
for ( var commit of evnt.payload.commits ) {
let e = document.createElement( 'li' );
e.innerHTML = '<a href="https://github.com/SapphireMordred/Sapphire/commit/' + commit.sha + '">' + commit.message + '</a>';
commitContainer.appendChild( e );
}
}
});
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

View file

@ -9,10 +9,18 @@ else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
# edit and continue
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Disabling /SAFESEH")
# disabling SAFESEH
message(STATUS "Disabling Safe Exception Handlers..")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
# edit and continue
message(STATUS "Enabling Edit and Continue..")
add_definitions(/ZI)
# incremental linking
message(STATUS "Enabling Incremental Linking..")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /INCREMENTAL")
endif()
endif()

View file

@ -40,7 +40,7 @@ class CmnDefInnBedDef
def Scene00100( player ) //Wake up
{
player.eventPlay( this.id, 100, 0xF32E48F8/*flags*/, 0/*unk*/, 0/*unk*/,
player.eventPlay( this.id, 100, 0x800/*flags*/, 0/*unk*/, 0/*unk*/,
fun( player, eventId, param1, param2, param3 )
{

View file

@ -23,7 +23,7 @@ DROP TABLE IF EXISTS `infolinkshell`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `infolinkshell` (
`LinkshellId` int(20) NOT NULL AUTO_INCREMENT,
`LinkshellId` bigint(20) NOT NULL AUTO_INCREMENT,
`MasterCharacterId` int(20) DEFAULT NULL,
`CharacterIdList` blob,
`LinkshellName` varchar(32) DEFAULT NULL,

View file

@ -572,7 +572,8 @@ namespace Core {
MpGain = 11,
TpLoss = 12,
TpGain = 13,
GpGain = 14
GpGain = 14,
Mount = 38
};
enum class ActionHitSeverityType : uint8_t
@ -921,10 +922,11 @@ namespace Core {
GearSetEquipMsg = 0x321,
ToggleOrchestrionUnlock = 0x396
ToggleOrchestrionUnlock = 0x396,
Dismount = 0x3a0
};
enum struct ChatType : uint32_t
enum struct ChatType : uint16_t
{
LogKindError,
ServerDebug,
@ -1038,6 +1040,12 @@ namespace Core {
Visor = 0x40,
};
enum SkillType : uint8_t
{
Normal = 0x1,
MountSkill = 0xD,
};
struct ServerEntry
{
uint32_t serverId;

View file

@ -93,7 +93,7 @@ std::string Core::Util::base64_decode( std::string const& encoded_string ) {
char_array_4[i++] = encoded_string[in_]; in_++;
if( i == 4 ) {
for( i = 0; i < 4; i++ )
char_array_4[i] = base64_chars.find( char_array_4[i] );
char_array_4[i] = static_cast< uint8_t >( base64_chars.find( char_array_4[i] ) );
char_array_3[0] = ( char_array_4[0] << 2 ) + ( ( char_array_4[1] & 0x30 ) >> 4 );
char_array_3[1] = ( ( char_array_4[1] & 0xf ) << 4 ) + ( ( char_array_4[2] & 0x3c ) >> 2 );
@ -110,7 +110,7 @@ std::string Core::Util::base64_decode( std::string const& encoded_string ) {
char_array_4[j] = 0;
for( j = 0; j < 4; j++ )
char_array_4[j] = base64_chars.find( char_array_4[j] );
char_array_4[j] = static_cast< uint8_t >( base64_chars.find( char_array_4[j] ) );
char_array_3[0] = ( char_array_4[0] << 2 ) + ( ( char_array_4[1] & 0x30 ) >> 4 );
char_array_3[1] = ( ( char_array_4[1] & 0xf ) << 4 ) + ( ( char_array_4[2] & 0x3c ) >> 2 );

View file

@ -88,7 +88,7 @@ bool Core::Data::ExdData::loadZoneInfo()
uint16_t weather_rate = getField< uint16_t >( fields, 10 ) > 75 ? 0 : getField< uint16_t >( fields, 10 );
auto weatherRateFields = weatherRate.get_row( weather_rate );
int32_t aetheryte_index = getField< int32_t >( fields, 20 );
int32_t aetheryte_index = getField< int32_t >( fields, 23 );
ZoneInfo info{ 0 };

View file

@ -5,6 +5,7 @@
#include <time.h>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include "Server_Common/Util/Util.h"
Core::Network::Packets::GamePacket::GamePacket( uint16_t subType, uint16_t size, uint32_t id1, uint32_t id2, uint16_t type )
{
@ -97,16 +98,5 @@ void Core::Network::Packets::GamePacket::savePacket()
std::string Core::Network::Packets::GamePacket::toString() const
{
std::string str = "\n";
for( uint32_t i = 0; i < getSize(); i++ )
{
str += boost::str( boost::format( "%|02X|" ) % ( int32_t ) ( m_dataBuf[i] & 0xFF ) ) + " ";
if( ( i + 1 ) % 16 == 0 )
str += "\n";
}
str += "\n";
return str;
return Core::Util::binaryToHexDump( const_cast<uint8_t *>( &m_dataBuf[0] ), getSize() );
}

View file

@ -46,7 +46,7 @@ namespace Packets {
{
Ping = 0x0065,
Init = 0x0066,
Chat = 0x0067,
Chat = 0x00B9,
ChatBanned = 0x006B,
Logout = 0x0077,
CFNotify = 0x0078,
@ -73,15 +73,13 @@ namespace Packets {
Effect = 0x00FB, // updated 4.1
GCAffiliation = 0x00FC,
HateList = 0x011A,
PlayerSpawn = 0x011C, // updated 4.1
NpcSpawn = 0x011D, // updated 4.1
ActorMove = 0x011E, // updated 4.1
UpdateClassInfo = 0x111D,
ActorSetPos = 0x0120, // updated 4.1
ActorCast = 0x0123, // updated 4.1
HateList = 0x0126, // updated 4.1
UpdateClassInfo = 0x012A, // updated 4.1
InitUI = 0x012B, // updated 4.1
ActorOwner = 0x012D, // updated 4.1
PlayerStats = 0x0138, // updated 4.1
@ -103,9 +101,6 @@ namespace Packets {
InventoryActionAck = 0x1139,
UpdateInventorySlot = 0x0153, // updated 4.1
EventPlay = 0x0160, // updated 4.1
EventStart = 0x0169, // updated 4.1
EventFinish = 0x016A, // updated 4.1
@ -119,11 +114,11 @@ namespace Packets {
QuestFinish = 0x0180, // updated 4.1
QuestTracker = 0x018D, // updated 4.1
ActorSpawn = 0x0190, // todo: split into playerspawn/actorspawn and use opcode 0x110/0x111
ActorFreeSpawn = 0x0191, // unchanged for sb
InitZone = 0x019A, // unchanged for sb
Mount = 0x019F,
WeatherChange = 0x01AF, // updated for sb
PlayerTitleList = 0x01BD, // updated for 4.1
Discovery = 0x01BE, // updated for 4.1

View file

@ -42,8 +42,8 @@ struct FFXIVIpcInit : FFXIVIpcBasePacket<Init>
*/
struct FFXIVIpcChat : FFXIVIpcBasePacket<Chat>
{
/* 0000 */ Common::ChatType chatType;
uint8_t padding[16]; //Maybe this is SubCode, or some kind of talker ID...
/* 0000 */ uint8_t padding[14]; //Maybe this is SubCode, or some kind of talker ID...
Common::ChatType chatType;
char name[32];
char msg[1012];
};
@ -81,7 +81,7 @@ struct FFXIVIpcPlayTime : FFXIVIpcBasePacket<Playtime>
*/
struct PlayerEntry {
uint64_t contentId;
char bytes[12];
uint8_t bytes[12];
uint16_t zoneId;
uint16_t zoneId1;
char bytes1[8];
@ -604,7 +604,8 @@ struct FFXIVIpcActorSetPos : FFXIVIpcBasePacket<ActorSetPos>
struct FFXIVIpcActorCast : FFXIVIpcBasePacket<ActorCast>
{
uint16_t action_id;
uint16_t unknown;
Common::SkillType skillType;
uint8_t unknown;
uint32_t unknown_1; // Also action id
float cast_time;
uint32_t target_id;
@ -1303,6 +1304,15 @@ struct FFXIVIpcEquipDisplayFlags : FFXIVIpcBasePacket<EquipDisplayFlags>
uint8_t bitmask;
};
/**
* Structural representation of the packet sent by the server
* to mount a player
*/
struct FFXIVIpcMount : FFXIVIpcBasePacket<Mount>
{
uint32_t id;
};
} /* Server */
} /* Packets */

View file

@ -1,9 +1,9 @@
#include "Util.h"
#include <chrono>
#include <boost/variant/detail/substitute.hpp>
std::string Core::Util::binaryToHexString( uint8_t* pBinData, uint16_t size )
{
std::string outStr;
for( uint32_t i = 0; i < size; i++ )
@ -15,6 +15,67 @@ std::string Core::Util::binaryToHexString( uint8_t* pBinData, uint16_t size )
}
std::string Core::Util::binaryToHexDump( uint8_t* pBinData, uint16_t size )
{
int bytesPerLine = 16;
constexpr char hexChars[] = "0123456789ABCDEF";
int offsetBlock = 8 + 3;
int byteBlock = offsetBlock + bytesPerLine * 3 + ( bytesPerLine - 1 ) / 8 + 2;
int lineLength = byteBlock + bytesPerLine + 1;
std::string line ( lineLength, ' ' );
int numLines = ( size + bytesPerLine - 1 ) / bytesPerLine;
std::string outStr;
for( uint32_t i = 0; i < size; i += bytesPerLine )
{
line[0] = hexChars[( i >> 28 ) & 0xF];
line[1] = hexChars[( i >> 24 ) & 0xF];
line[2] = hexChars[( i >> 20 ) & 0xF];
line[3] = hexChars[( i >> 16 ) & 0xF];
line[4] = hexChars[( i >> 12 ) & 0xF];
line[5] = hexChars[( i >> 8 ) & 0xF];
line[6] = hexChars[( i >> 4 ) & 0xF];
line[7] = hexChars[( i >> 0 ) & 0xF];
int hexColumn = offsetBlock;
int charColumn = byteBlock;
for( int j = 0; j < bytesPerLine; j++ )
{
if( j > 0 && ( j & 7 ) == 0)
{
hexColumn++;
}
if( i + j >= size )
{
line[hexColumn] = ' ';
line[hexColumn + 1] = ' ';
line[charColumn] = ' ';
}
else
{
uint8_t by = pBinData[i + j];
line[hexColumn] = hexChars[( by >> 4 ) & 0xF];
line[hexColumn + 1] = hexChars[by & 0xF];
line[charColumn] = by < 32 ? '.' : static_cast<char>( by );
}
hexColumn += 3;
charColumn++;
}
outStr += line + "\n";
}
return outStr;
}
uint64_t Core::Util::getTimeMs()
{
std::chrono::milliseconds epoch = std::chrono::duration_cast< std::chrono::milliseconds >(std::chrono::system_clock::now().time_since_epoch());

View file

@ -9,6 +9,8 @@ namespace Util {
std::string binaryToHexString( uint8_t* pBinData, uint16_t size );
std::string binaryToHexDump( uint8_t* pBinData, uint16_t size );
uint64_t getTimeMs();
uint64_t getTimeSeconds();

View file

@ -20,11 +20,6 @@ set_target_properties(server_lobby PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
)
if(MSVC)
message(STATUS "Enabling Edit and Continue..")
set_property(TARGET server_lobby APPEND_STRING PROPERTY COMPILE_FLAGS " /INCREMENTAL /ZI")
endif()
if (UNIX)
target_link_libraries(server_lobby Common xivdat pthread mysqlclient mysqlConnector dl z)
else()

View file

@ -152,7 +152,7 @@ void Core::Network::GameConnection::getCharList( FFXIVARR_PACKET_RAW& packet, ui
auto charList = g_restConnector.getCharList( ( char * )m_pSession->getSessionId() );
int32_t charIndex = 0;
uint32_t charIndex = 0;
for( uint8_t i = 0; i < 4; i++ )
{

View file

@ -6,6 +6,7 @@
#include <src/servers/Server_Common/Network/Hive.h>
#include <src/servers/Server_Common/Network/Acceptor.h>
#include <src/servers/Server_Common/Version.h>
#include <src/servers/Server_Common/Logging/Logger.h>
#include <src/servers/Server_Common/Config/XMLConfig.h>
@ -58,7 +59,8 @@ namespace Core {
g_log.info( "===========================================================" );
g_log.info( "Sapphire Server Project " );
g_log.info( "Version: x.y.z" );
g_log.info( "Version: " + Version::VERSION );
g_log.info( "Git Hash: " + Version::GIT_HASH );
g_log.info( "Compiled: " __DATE__ " " __TIME__ );
g_log.info( "===========================================================" );
@ -89,7 +91,7 @@ namespace Core {
return false;
}
std::vector<std::string> args( argv + 1, argv + argc );
for( auto i = 0; i + 1 < args.size(); i += 2 )
for( size_t i = 0; i + 1 < args.size(); i += 2 )
{
std::string arg( "" );
std::string val( "" );

View file

@ -266,7 +266,7 @@ namespace SimpleWeb {
if( content_length>num_additional_bytes ) {
auto timer = get_timeout_timer();
boost::asio::async_read( *socket, response->content_buffer,
boost::asio::transfer_exactly( content_length - num_additional_bytes ),
boost::asio::transfer_exactly( static_cast< size_t >( content_length - num_additional_bytes ) ),
[this, timer]( const boost::system::error_code& ec, size_t /*bytes_transferred*/ ) {
if( timer )
timer->cancel();
@ -307,7 +307,7 @@ namespace SimpleWeb {
line.pop_back();
std::streamsize length = stol( line, 0, 16 );
auto num_additional_bytes = static_cast<std::streamsize>( response->content_buffer.size() - bytes_transferred );
auto num_additional_bytes = response->content_buffer.size() - bytes_transferred;
auto post_process = [this, &response, &streambuf, length] {
std::ostream stream( &streambuf );
@ -332,7 +332,7 @@ namespace SimpleWeb {
if( ( 2 + length )>num_additional_bytes ) {
auto timer = get_timeout_timer();
boost::asio::async_read( *socket, response->content_buffer,
boost::asio::transfer_exactly( 2 + length - num_additional_bytes ),
boost::asio::transfer_exactly( static_cast< size_t >( 2 + length - num_additional_bytes ) ),
[this, post_process, timer]( const boost::system::error_code& ec, size_t /*bytes_transferred*/ ) {
if( timer )
timer->cancel();

View file

@ -19,11 +19,6 @@ set_target_properties(server_rest PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
)
if(MSVC)
message(STATUS "Enabling Edit and Continue..")
set_property(TARGET server_rest APPEND_STRING PROPERTY COMPILE_FLAGS " /INCREMENTAL /ZI")
endif()
if (UNIX)
target_link_libraries (server_rest Common xivdat pthread mysqlclient mysqlConnector dl z)
else()

View file

@ -171,7 +171,7 @@ namespace Core {
uint16_t size = static_cast< uint16_t >( m_lookMap.size() );
for( int32_t i = 0; i < m_lookMap.size(); i++ )
for( uint32_t i = 0; i < m_lookMap.size(); i++ )
{
customize[i] = m_lookMap[i];
}

View file

@ -78,7 +78,7 @@ bool loadSettings( int32_t argc, char* argv[] )
}
std::vector<std::string> args( argv + 1, argv + argc );
for( auto i = 0; i + 1 < args.size(); i += 2 )
for( size_t i = 0; i + 1 < args.size(); i += 2 )
{
std::string arg( "" );
std::string val( "" );
@ -171,7 +171,7 @@ bool loadSettings( int32_t argc, char* argv[] )
params.port = m_pConfig->getValue< uint16_t >( "Settings.General.Mysql.Port", 3306 );
params.username = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Username", "root" );
server.config.port = std::stoul( m_pConfig->getValue<std::string>( "Settings.General.HttpPort", "80" ) );
server.config.port = static_cast< unsigned short >( std::stoul( m_pConfig->getValue<std::string>( "Settings.General.HttpPort", "80" ) ) );
if( !g_database.initialize( params ) )
{
@ -619,7 +619,7 @@ int main(int argc, char* argv[])
auto path = boost::filesystem::canonical( web_root_path / "news.xml" );
//Check if path is within web_root_path
if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) ||
!equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
!std::equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
throw invalid_argument( "path must be within root path" );
if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) )
throw invalid_argument( "file does not exist" );
@ -660,7 +660,7 @@ int main(int argc, char* argv[])
auto path = boost::filesystem::canonical( web_root_path / "headlines.xml" );
//Check if path is within web_root_path
if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) ||
!equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
!std::equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
throw invalid_argument( "path must be within root path" );
if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) )
throw invalid_argument( "file does not exist" );
@ -705,7 +705,7 @@ int main(int argc, char* argv[])
auto path = boost::filesystem::canonical( web_root_path / request->path );
//Check if path is within web_root_path
if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) ||
!equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
!std::equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
throw invalid_argument( "path must be within root path" );
if( boost::filesystem::is_directory( path ) )
path /= "index.html";
@ -731,7 +731,7 @@ int main(int argc, char* argv[])
else
throw invalid_argument( "could not read file" );
}
catch( const exception &e )
catch( const exception & )
{
string content = "Path not found: " + request->path;
*response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content;

View file

@ -273,7 +273,7 @@ namespace SimpleWeb {
try {
content_length=stoull(it->second);
}
catch(const std::exception &e) {
catch( const std::exception & ) {
if(on_error)
on_error(request, boost::system::error_code(boost::system::errc::protocol_error, boost::system::generic_category()));
return;
@ -282,7 +282,7 @@ namespace SimpleWeb {
//Set timeout on the following boost::asio::async-read or write function
auto timer=this->get_timeout_timer(socket, config.timeout_content);
boost::asio::async_read(*socket, request->streambuf,
boost::asio::transfer_exactly(content_length-num_additional_bytes),
boost::asio::transfer_exactly(static_cast< size_t >(content_length-num_additional_bytes)),
[this, socket, request, timer]
(const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
if(timer)
@ -388,7 +388,7 @@ namespace SimpleWeb {
try {
http_version=stof(request->http_version);
}
catch(const std::exception &e){
catch( const std::exception & ){
if(on_error)
on_error(request, boost::system::error_code(boost::system::errc::protocol_error, boost::system::generic_category()));
return;
@ -410,7 +410,7 @@ namespace SimpleWeb {
try {
resource_function(response, request);
}
catch(const std::exception &e) {
catch( const std::exception & ) {
if(on_error)
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled, boost::system::generic_category()));
return;

View file

@ -53,7 +53,7 @@ void Core::Action::ActionCast::onStart()
GamePacketNew< FFXIVIpcActorCast, ServerZoneIpcType > castPacket( m_pSource->getId() );
castPacket.data().action_id = m_id;
castPacket.data().unknown = 1;
castPacket.data().skillType = Common::SkillType::Normal;
castPacket.data().unknown_1 = m_id;
castPacket.data().cast_time = static_cast< float >( m_castTime / 1000 ); // This is used for the cast bar above the target bar of the caster.
castPacket.data().target_id = m_pTarget->getId();

View file

@ -0,0 +1,111 @@
#include "ActionMount.h"
#include <src/servers/Server_Common/Common.h>
#include <src/servers/Server_Common/Util/Util.h>
#include <src/servers/Server_Common/Util/UtilMath.h>
#include <src/servers/Server_Common/Exd/ExdData.h>
#include <src/servers/Server_Common/Logging/Logger.h>
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket142.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket144.h"
#include "src/servers/Server_Zone/Actor/Player.h"
#include "src/servers/Server_Zone/Script/ScriptManager.h"
using namespace Core::Common;
using namespace Core::Network;
using namespace Core::Network::Packets;
using namespace Core::Network::Packets::Server;
extern Core::Data::ExdData g_exdData;
extern Core::Logger g_log;
extern Core::Scripting::ScriptManager g_scriptMgr;
Core::Action::ActionMount::ActionMount()
{
m_handleActionType = Common::HandleActionType::Event;
}
Core::Action::ActionMount::ActionMount( Entity::ActorPtr pActor, uint32_t mountId )
{
m_startTime = 0;
m_id = mountId;
m_handleActionType = HandleActionType::Spell;
m_castTime = 1000;
m_pSource = pActor;
m_bInterrupt = false;
}
Core::Action::ActionMount::~ActionMount()
{
}
void Core::Action::ActionMount::onStart()
{
if( !m_pSource )
return;
m_pSource->getAsPlayer()->sendDebug( "ActionMount::onStart()" );
m_startTime = Util::getTimeMs();
GamePacketNew< FFXIVIpcActorCast, ServerZoneIpcType > castPacket( m_pSource->getId() );
castPacket.data().action_id = m_id;
castPacket.data().skillType = Common::SkillType::MountSkill;
castPacket.data().unknown_1 = m_id;
castPacket.data().cast_time = static_cast< float >( m_castTime / 1000 ); // This is used for the cast bar above the target bar of the caster.
castPacket.data().target_id = m_pSource->getAsPlayer()->getId();
m_pSource->sendToInRangeSet( castPacket, true );
m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Casting );
m_pSource->getAsPlayer()->sendStateFlags();
}
void Core::Action::ActionMount::onFinish()
{
if( !m_pSource )
return;
auto pPlayer = m_pSource->getAsPlayer();
pPlayer->sendDebug( "ActionMount::onFinish()" );
pPlayer->unsetStateFlag( PlayerStateFlag::Casting );
pPlayer->sendStateFlags();
GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket(pPlayer->getId());
effectPacket.data().targetId = pPlayer->getId();
effectPacket.data().actionAnimationId = m_id;
effectPacket.data().unknown_62 = 13; // Affects displaying action name next to number in floating text
effectPacket.data().actionTextId = 4;
effectPacket.data().numEffects = 1;
effectPacket.data().rotation = Math::Util::floatToUInt16Rot(pPlayer->getRotation());
effectPacket.data().effectTarget = INVALID_GAME_OBJECT_ID;
effectPacket.data().effects[0].effectType = ActionEffectType::Mount;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::CritDamage;
effectPacket.data().effects[0].value = m_id;
pPlayer->sendToInRangeSet(effectPacket, true);
pPlayer->mount( m_id );
}
void Core::Action::ActionMount::onInterrupt()
{
if( !m_pSource )
return;
m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 );
m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Casting );
m_pSource->getAsPlayer()->sendStateFlags();
auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt,
0x219, 1, m_id, 0 );
// Note: When cast interrupt from taking too much damage, set the last value to 1. This enables the cast interrupt effect. Example:
// auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, 0x219, 1, m_id, 0 );
m_pSource->sendToInRangeSet( control, true );
}

View file

@ -0,0 +1,28 @@
#ifndef _ACTIONMOUNT_H_
#define _ACTIONMOUNT_H_
#include "src/servers/Server_Zone/Forwards.h"
#include "Action.h"
namespace Core { namespace Action {
class ActionMount : public Action
{
private:
public:
ActionMount();
~ActionMount();
ActionMount( Entity::ActorPtr pActor, uint32_t mountId );
void onStart() override;
void onFinish() override;
void onInterrupt() override;
};
}
}
#endif

View file

@ -685,7 +685,7 @@ void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint32_t actionId, u
case ActionEffectType::Damage:
{
effectPacket.data().effects[0].value = param1;
effectPacket.data().effects[0].value = static_cast< uint16_t >( param1 );
effectPacket.data().effects[0].effectType = ActionEffectType::Damage;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage;
effectPacket.data().effects[0].unknown_3 = 7;

View file

@ -451,7 +451,7 @@ void Core::Entity::BattleNpc::onDeath()
// todo: this is actually retarded, we need real rand()
srand( time( NULL ) );
srand( static_cast< unsigned int> ( time( NULL ) ) );
auto pPlayer = pHateEntry->m_pActor->getAsPlayer();
pPlayer->gainExp( exp );

View file

@ -38,7 +38,7 @@ float CalcBattle::calculateBaseStat( PlayerPtr pPlayer )
// SB Base Stat Formula (Aligned)
if ( level > 60 )
{
base = ( ( ( level == 61 ) ? 224 : 220 ) + ( level - 61 ) * 8);
base = static_cast< float >( ( ( ( level == 61 ) ? 224 : 220 ) + ( level - 61 ) * 8) );
}
// HW Base Stat Formula (Aligned)
else if ( level > 50 )
@ -77,7 +77,7 @@ uint32_t CalcBattle::calculateMaxHp( PlayerPtr pPlayer )
// These values are not precise.
if ( level >= 60 )
approxBaseHp = 2600 + ( level - 60 ) * 100;
approxBaseHp = static_cast< float >( 2600 + ( level - 60 ) * 100 );
else if ( level >= 50 )
approxBaseHp = 1700 + ( ( level - 50 ) * ( 1700 * 1.04325f ) );
else

View file

@ -347,7 +347,7 @@ void Core::Entity::Player::teleport( uint16_t aetheryteId, uint8_t type )
void Core::Entity::Player::forceZoneing( uint32_t zoneId )
{
m_queuedZoneing = boost::make_shared< QueuedZoning >( zoneId, getPos(), Util::getTimeMs(), 0 );
m_queuedZoneing = boost::make_shared< QueuedZoning >( zoneId, getPos(), Util::getTimeMs(), 0.f );
//performZoning( zoneId, Common::ZoneingType::None, getPos() );
}
@ -1439,6 +1439,33 @@ uint8_t Core::Entity::Player::getEquipDisplayFlags() const
return m_equipDisplayFlags;
}
void Core::Entity::Player::mount( uint32_t id )
{
// TODO: Fix me for SQL rewrite
/* m_mount = id;
sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Mounted )), true );
sendToInRangeSet( ActorControlPacket143( getId(), 0x39e, 12 ), true ); //?
GamePacketNew< FFXIVIpcMount, ServerZoneIpcType > mountPacket( getId() );
mountPacket.data().id = m_mount;
sendToInRangeSet( mountPacket, true );*/
}
void Core::Entity::Player::dismount()
{
// TODO: Fix me for SQL rewrite
/* sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Idle )), true );
sendToInRangeSet( ActorControlPacket143( getId(), ActorControlType::Dismount, 1 ), true );
m_mount = 0;*/
}
uint8_t Core::Entity::Player::getCurrentMount() const
{
// TODO: Fix me for SQL rewrite
// return m_mount;
return 0;
}
void Core::Entity::Player::autoAttack( ActorPtr pTarget )
{

View file

@ -197,9 +197,9 @@ public:
// Inventory / Item / Currency
//////////////////////////////////////////////////////////////////////////////////////////////////////
/*! add an item to the first free slot in one of the 4 main containers */
bool tryAddItem( uint16_t catalogId, uint16_t quantity );
bool tryAddItem( uint16_t catalogId, uint32_t quantity );
/*! add an item to a given container */
bool addItem( uint16_t containerId, uint16_t catalogId, uint16_t quantity );
bool addItem( uint16_t containerId, uint16_t catalogId, uint32_t quantity );
/*! equip an item to a specified slot */
void equipItem( Inventory::EquipSlot equipSlotId, ItemPtr pItem, bool sendModel );
/*! remove an item from an equipment slot */
@ -338,8 +338,14 @@ public:
void setTitle( uint16_t titleId );
/*! change gear param state */
void setEquipDisplayFlags( uint8_t state );
/*! get gear param state and send update to inRangeSet */
/*! get gear param state */
uint8_t getEquipDisplayFlags() const;
/*! mount the specified mount and send the packets */
void mount( uint32_t id );
/*! dismount the current mount and send the packets */
void dismount();
/*! get the current mount */
uint8_t getCurrentMount() const;
void calculateStats() override;
void sendStats();

View file

@ -189,10 +189,10 @@ void Core::Entity::Player::removeCrystal( uint8_t type, uint32_t amount )
queuePacket( invUpPacket );
}
bool Core::Entity::Player::tryAddItem( uint16_t catalogId, uint16_t quantity )
bool Core::Entity::Player::tryAddItem( uint16_t catalogId, uint32_t quantity )
{
for( uint8_t i = 0; i < 4; i++ )
for( uint16_t i = 0; i < 4; i++ )
{
if( m_pInventory->addItem( i, -1, catalogId, quantity ) != -1 )
{
@ -202,7 +202,7 @@ bool Core::Entity::Player::tryAddItem( uint16_t catalogId, uint16_t quantity )
return false;
}
bool Core::Entity::Player::addItem( uint16_t containerId, uint16_t catalogId, uint16_t quantity )
bool Core::Entity::Player::addItem( uint16_t containerId, uint16_t catalogId, uint32_t quantity )
{
if( m_pInventory->addItem( containerId, -1, catalogId, quantity ) != -1 )
return true;

View file

@ -864,7 +864,6 @@ void Core::Entity::Player::updateQuest( uint16_t questId, uint8_t sequence )
{
if( hasQuest( questId ) )
{
uint8_t index = getQuestIndex( questId );
auto pNewQuest = m_activeQuests[index];
GamePacketNew< FFXIVIpcQuestUpdate, ServerZoneIpcType > pe_qa( getId() );
@ -926,7 +925,7 @@ void Core::Entity::Player::sendQuestTracker()
if( m_questTracking[ii] >= 0 )
{
trackerPacket.data().entry[ii].active = 1;
trackerPacket.data().entry[ii].questIndex = m_questTracking[ii];
trackerPacket.data().entry[ii].questIndex = static_cast< uint8_t >( m_questTracking[ii] );
}
}
queuePacket( trackerPacket );
@ -1030,7 +1029,7 @@ bool Core::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t optional
exp = questInfo->reward_exp_factor;
uint16_t rewardItemCount = questInfo->reward_item.size();
auto rewardItemCount = questInfo->reward_item.size();
uint16_t optionalItemCount = questInfo->reward_item_optional.size() > 0 ? 1 : 0;
uint32_t gilReward = questInfo->reward_gil;

View file

@ -35,11 +35,6 @@ set_target_properties(server_zone PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
)
if(MSVC)
message(STATUS "Enabling Edit and Continue..")
set_property(TARGET server_zone APPEND_STRING PROPERTY COMPILE_FLAGS " /INCREMENTAL /ZI")
endif()
if (UNIX)
target_link_libraries ( server_zone Common xivdat pthread mysqlclient mysqlConnector dl z )
else()

View file

@ -44,12 +44,13 @@ Core::DebugCommandHandler::DebugCommandHandler()
registerCommand( "set", &DebugCommandHandler::set, "Loads and injects a premade Packet.", 1 );
registerCommand( "get", &DebugCommandHandler::get, "Loads and injects a premade Packet.", 1 );
registerCommand( "add", &DebugCommandHandler::add, "Loads and injects a premade Packet.", 1 );
registerCommand( "inject", &DebugCommandHandler::injectPacket, "Loads and injects a premade Packet.", 1 );
registerCommand( "injectc", &DebugCommandHandler::injectChatPacket, "Loads and injects a premade Packet.", 1 );
registerCommand( "script_reload", &DebugCommandHandler::scriptReload, "Loads and injects a premade Packet.", 1 );
registerCommand( "inject", &DebugCommandHandler::injectPacket, "Loads and injects a premade packet.", 1 );
registerCommand( "injectc", &DebugCommandHandler::injectChatPacket, "Loads and injects a premade chat packet.", 1 );
registerCommand( "script_reload", &DebugCommandHandler::scriptReload, "Reload all server scripts", 1 );
registerCommand( "nudge", &DebugCommandHandler::nudge, "Nudges you forward/up/down", 1 );
registerCommand( "info", &DebugCommandHandler::serverInfo, "Send server info", 0 );
registerCommand( "unlock", &DebugCommandHandler::unlockCharacter, "Unlock character", 1 );
registerCommand( "help", &DebugCommandHandler::help, "Shows registered commands", 0 );
}
// clear all loaded commands
@ -121,6 +122,18 @@ void Core::DebugCommandHandler::scriptReload( char * data, Core::Entity::PlayerP
pPlayer->sendDebug( "Scripts reloaded." );
}
void Core::DebugCommandHandler::help( char* data, Entity::PlayerPtr pPlayer, boost::shared_ptr< Core::DebugCommand > command )
{
pPlayer->sendDebug( "Registered debug commands:" );
for ( auto cmd : m_commandMap )
{
if ( pPlayer->getGmRank( ) >= cmd.second->m_gmLevel )
{
pPlayer->sendDebug( " - " + cmd.first + " - " + cmd.second->getHelpText( ) );
}
}
}
void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr<Core::DebugCommand> command )
{
std::string subCommand = "";
@ -258,6 +271,14 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye
pPlayer->sendModel();
pPlayer->sendDebug( "Model updated" );
}
else if ( subCommand == "mount" )
{
int32_t id;
sscanf( params.c_str(), "%d", &id );
pPlayer->dismount();
pPlayer->mount( id );
}
else
{
pPlayer->sendUrgent( subCommand + " is not a valid SET command." );
@ -494,3 +515,8 @@ void Core::DebugCommandHandler::serverInfo( char * data, Core::Entity::PlayerPtr
pPlayer->sendDebug( "Compiled: " __DATE__ " " __TIME__ );
pPlayer->sendDebug( "Sessions: " + std::to_string( g_serverZone.getSessionCount() ) );
}
void Core::DebugCommandHandler::unlockCharacter( char* data, Entity::PlayerPtr pPlayer, boost::shared_ptr< Core::DebugCommand > command )
{
pPlayer->unlock( );
}

View file

@ -27,6 +27,9 @@ public:
// execute command if registered
void execCommand( char * data, Entity::PlayerPtr pPlayer );
// help command
void help( char* data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
// command handler callbacks
void set( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
void get( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
@ -39,6 +42,9 @@ public:
void nudge( char* data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
void serverInfo( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
void unlockCharacter( char* data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
void targetInfo( char* data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
};
}

View file

@ -44,6 +44,7 @@ namespace Core
TYPE_FORWARD( Action );
TYPE_FORWARD( ActionTeleport );
TYPE_FORWARD( ActionCast );
TYPE_FORWARD( ActionMount );
TYPE_FORWARD( EventAction );
}

View file

@ -108,7 +108,7 @@ Core::Inventory::InvSlotPairVec Core::Inventory::getSlotsOfItemsInInventory( uin
for( auto item : inv->getItemMap() )
{
if( item.second && item.second->getId() == catalogId )
outVec.push_back( std::make_pair( i, item.first ) );
outVec.push_back( std::make_pair( i, static_cast< int8_t >( item.first ) ) );
}
}
return outVec;
@ -118,7 +118,7 @@ Core::Inventory::InvSlotPair Core::Inventory::getFreeBagSlot()
{
for( auto i : { Bag0, Bag1, Bag2, Bag3 } )
{
int16_t freeSlot = m_inventoryMap[i]->getFreeSlot();
auto freeSlot = static_cast< int8_t >( m_inventoryMap[i]->getFreeSlot() );
if( freeSlot != -1 )
return std::make_pair( i, freeSlot );
@ -457,7 +457,7 @@ int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t
return -1;
}
int16_t rSlotId = -1;
int8_t rSlotId = -1;
//if( itemInfo->stack_size > 1 )
//{

View file

@ -29,7 +29,7 @@ bool Core::LinkshellMgr::loadLinkshells()
do
{
uint32_t linkshellId = field[0].get< uint32_t >();
uint64_t linkshellId = field[0].get< uint64_t >();
uint32_t masterId = field[1].get< uint32_t >();
std::string name = field[3].getString();

View file

@ -12,7 +12,7 @@ typedef boost::shared_ptr< Linkshell > LinkshellPtr;
class LinkshellMgr
{
private:
std::map< uint32_t, LinkshellPtr > m_linkshellIdMap;
std::map< uint64_t, LinkshellPtr > m_linkshellIdMap;
std::map< std::string, LinkshellPtr > m_linkshellNameMap;
LinkshellPtr getLinkshellByName( const std::string& name );

View file

@ -206,7 +206,7 @@ void Core::Network::GameConnection::handleZonePacket( const Packets::GamePacket&
g_log.debug( sessionStr + " Undefined Zone IPC : Unknown ( " +
boost::str( boost::format( "%|04X|" ) %
static_cast< uint32_t >( pPacket.getSubType() & 0xFFFF ) ) + " )" );
g_log.debug( pPacket.toString() );
g_log.debug( "\n" + pPacket.toString() );
}
}

View file

@ -106,6 +106,11 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in
pPlayer->changeTarget( targetId );
break;
}
case 0x65:
{
pPlayer->dismount();
break;
}
case 0x68: // Remove status (clicking it off)
{
// todo: check if status can be removed by client from exd
@ -120,7 +125,7 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in
}
case 0x12E: // Set player title
{
pPlayer->setTitle( param1 );
pPlayer->setTitle( static_cast< uint16_t >( param1 ) );
break;
}
case 0x12F: // Get title list
@ -211,8 +216,8 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in
auto fromAetheryte = g_exdData.getAetheryteInfo( g_exdData.m_zoneInfoMap[pPlayer->getZoneId()].aetheryte_index );
// calculate cost - does not apply for favorite points or homepoints neither checks for aether tickets
auto cost = ( sqrt( pow( fromAetheryte->map_coord_x - targetAetheryte->map_coord_x, 2 ) +
pow( fromAetheryte->map_coord_y - targetAetheryte->map_coord_y, 2 ) ) / 2 ) + 100;
auto cost = static_cast< uint16_t > ( ( sqrt( pow( fromAetheryte->map_coord_x - targetAetheryte->map_coord_x, 2 ) +
pow( fromAetheryte->map_coord_y - targetAetheryte->map_coord_y, 2 ) ) / 2 ) + 100 );
// cap at 999 gil
cost = cost > 999 ? 999 : cost;

View file

@ -141,7 +141,7 @@ void Core::Network::GameConnection::eventHandler( const Packets::GamePacket& inP
GamePacketNew< FFXIVIpcEventLinkshell, ServerZoneIpcType > linkshellEvent( pPlayer->getId() );
linkshellEvent.data().eventId = eventId;
linkshellEvent.data().scene = subEvent;
linkshellEvent.data().scene = static_cast< uint8_t >(subEvent);
linkshellEvent.data().param3 = 1;
linkshellEvent.data().unknown1 = 0x15a;
pPlayer->queuePacket( linkshellEvent );

View file

@ -282,13 +282,16 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac
}
case GmCommand::Teri:
{
if( param1 < 128 )
pPlayer->sendUrgent( "Zone ID out of range." );
auto zoneInfo = g_zoneMgr.getZone( param1 );
if ( !zoneInfo )
{
pPlayer->sendUrgent( "Invalid zone " + std::to_string( param1 ) );
}
else
{
targetPlayer->setPosition( targetPlayer->getPos() );
targetPlayer->performZoning( param1, targetPlayer->getPos(), 0 );
pPlayer->sendNotice( targetPlayer->getName() + " was warped to Zone " + std::to_string( param1 ) );
pPlayer->sendNotice( targetPlayer->getName() + " was warped to zone " + std::to_string( param1 ) + " (" + zoneInfo->getName( ) + ")" );
}
break;
}

View file

@ -25,9 +25,10 @@
#include "src/servers/Server_Zone/Forwards.h"
#include "src/servers/Server_Zone/Action/Action.h"
#include "src/servers/Server_Zone/Action/ActionTeleport.h"
#include "src/servers/Server_Zone/Action/ActionCast.h"
#include "src/servers/Server_Zone/Action/ActionMount.h"
#include "src/servers/Server_Zone/Script/ScriptManager.h"
#include "Server_Zone/Network/PacketWrappers/MoveActorPacket.h"
extern Core::Scripting::ScriptManager g_scriptMgr;
@ -41,12 +42,19 @@ using namespace Core::Network::Packets::Server;
void Core::Network::GameConnection::skillHandler( const Packets::GamePacket& inPacket,
Entity::PlayerPtr pPlayer )
{
uint8_t type = inPacket.getValAt< uint32_t >( 0x21 );
uint32_t action = inPacket.getValAt< uint32_t >( 0x24 );
uint32_t useCount = inPacket.getValAt< uint32_t >( 0x28 );
uint64_t targetId = inPacket.getValAt< uint64_t >( 0x30 );
pPlayer->sendDebug( "Skill type:" + std::to_string( type ) );
switch( type )
{
case Common::SkillType::Normal:
if( action < 1000000 ) // normal action
{
std::string actionIdStr = boost::str( boost::format( "%|04X|" ) % action );
@ -104,4 +112,19 @@ void Core::Network::GameConnection::skillHandler( const Packets::GamePacket& inP
}
break;
case Common::SkillType::MountSkill:
pPlayer->sendDebug( "Request mount " + std::to_string( action ) );
Action::ActionMountPtr pActionMount( new Action::ActionMount(pPlayer, action) );
pPlayer->setCurrentAction( pActionMount );
pPlayer->sendDebug("setCurrentAction()");
pPlayer->getCurrentAction()->onStart();
break;
}
}

View file

@ -64,6 +64,9 @@ private:
memcpy( m_data.orchestrionMask, player->getOrchestrionBitmask(), sizeof( m_data.orchestrionMask ) );
memset( m_data.mountGuideMask, 0xFF, sizeof( m_data.mountGuideMask) );
memset( m_data.fishingGuideMask, 0xFF, sizeof( m_data.fishingGuideMask ) );
memcpy( m_data.unlockBitmask, player->getUnlockBitmask(), sizeof( m_data.unlockBitmask ) );
memcpy( m_data.discovery, player->getDiscoveryBitmask(), sizeof( m_data.discovery ) );

View file

@ -111,6 +111,8 @@ namespace Server {
m_data.displayFlags |= Entity::Actor::DisplayFlags::Visor;
}
m_data.currentMount = pPlayer->getCurrentMount();
m_data.targetId = pPlayer->getTargetId();
//m_data.type = 1;
//m_data.unknown_33 = 4;

View file

@ -91,7 +91,7 @@ bool Core::Scripting::ScriptManager::onTalk( Core::Entity::PlayerPtr pPlayer, ui
std::string objName = Event::getEventName( eventId );
pPlayer->sendDebug("Actor: " +
std::to_string( actorId ) +
std::to_string( actorId ) + " -> " + std::to_string( Core::Event::mapEventActorToRealActor( static_cast< uint32_t >( actorId ) ) ) +
" \neventId: " +
std::to_string( eventId ) +
" (0x" + boost::str( boost::format( "%|08X|" )
@ -114,18 +114,18 @@ bool Core::Scripting::ScriptManager::onTalk( Core::Entity::PlayerPtr pPlayer, ui
}
catch( std::exception& e )
{
pPlayer->sendDebug( e.what( ) );
if( eventType == Common::EventType::Quest )
{
auto questInfo = g_exdData.getQuestInfo( eventId );
if( questInfo )
{
pPlayer->sendDebug( "Quest not implemented: " + questInfo->name + "\n" + e.what() );
pPlayer->sendUrgent( "Quest not implemented: " + questInfo->name );
return false;
}
}
pPlayer->sendDebug( e.what() );
return false;
}
return true;

View file

@ -52,6 +52,7 @@ int Core::Scripting::ScriptManager::init()
m_pChaiHandler->add( chaiscript::fun( &Entity::Player::returnToHomepoint ), "returnToHomepoint" );
m_pChaiHandler->add( chaiscript::fun( &Entity::Player::teleport ), "teleport" );
m_pChaiHandler->add( chaiscript::fun( &Entity::Player::prepareZoning ), "prepareZoning" );
m_pChaiHandler->add( chaiscript::fun( &Entity::Player::isInCombat ), "isInCombat" );
m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getCurrency ), "getCurrency" );
m_pChaiHandler->add( chaiscript::fun( &Entity::Player::addCurrency ), "addCurrency" );

View file

@ -4,6 +4,7 @@
#include "ServerZone.h"
#include <src/servers/Server_Common/Version.h>
#include <src/servers/Server_Common/Logging/Logger.h>
#include <src/servers/Server_Common/Config/XMLConfig.h>
#include <src/servers/Server_Common/Database/Database.h>
@ -375,7 +376,8 @@ void Core::ServerZone::printBanner() const
{
g_log.info("===========================================================" );
g_log.info( "Sapphire Server Project " );
g_log.info( "Version: x.y.z" );
g_log.info( "Version: " + Version::VERSION );
g_log.info( "Git Hash: " + Version::GIT_HASH );
g_log.info( "Compiled: " __DATE__ " " __TIME__ );
g_log.info( "===========================================================" );
}

View file

@ -27,6 +27,9 @@ if (UNIX)
target_link_libraries (exd_common_gen Common xivdat pthread mysqlclient dl z)
else()
target_link_libraries (exd_common_gen Common xivdat libmysql zlib1)
# ignore unchecked iterators warnings from msvc
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
endif()
target_link_libraries(exd_common_gen ${Boost_LIBRARIES} ${Boost_LIBRARIES})

View file

@ -1,23 +1,39 @@
set( CMAKE_CXX_FLAGS "-std=c++11 -m32")
cmake_minimum_required(VERSION 2.6)
cmake_policy(SET CMP0015 NEW)
project(Tool_pcb_reader2)
include_directories("../../")
set(SAPPHIRE_BOOST_VER 1.63.0)
set(SAPPHIRE_BOOST_FOLDER_NAME boost_1_63_0)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin/")
include_directories("../../lib/ChaiScript-6.0.0/include/")
include_directories("../../sapphire/datReader/")
include_directories("../../sapphire/")
include_directories("../")
file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*")
file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.cpp")
file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*")
SET(Boost_USE_STATIC_LIBS ON)
set(Boost_INCLUDE_DIR /opt/build_libs/boost_1_60_0)
set(Boost_LIBRARY_DIR /opt/build_libs/boost_1_60_0/stage/lib)
set(SERVER_COMMON_DIR ../../servers/Server_Common)
find_package(Boost COMPONENTS log log_setup thread date_time filesystem system REQUIRED)
include_directories(${Boost_INCLUDE_DIR})
link_directories(${Boost_LIBRARY_DIR})
link_directories(${SERVER_COMMON_DIR})
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ".")
add_executable(pcb_parser2 ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES})
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../bin/")
add_executable(pcb_reader2 ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES})
target_link_libraries (pcb_parser2 server_common.a pthread mysqlclient dl z)
target_link_libraries(pcb_parser2 ${Boost_LIBRARIES} ${Boost_LIBRARIES})
set_target_properties(pcb_reader2 PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../bin/"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../bin/"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../bin/"
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../bin/"
)
if (UNIX)
target_link_libraries (pcb_reader2 Common xivdat pthread mysqlclient dl z)
else()
target_link_libraries (pcb_reader2 Common xivdat libmysql zlib1)
endif()
target_link_libraries(pcb_reader2 ${Boost_LIBRARIES} ${Boost_LIBRARIES})

280
src/tools/pcb_reader/lgb.h Normal file
View file

@ -0,0 +1,280 @@
#ifndef _LGB_H
#define _LGB_H
#include <cstring>
#include <memory>
#include <cstdint>
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include "matrix4.h"
#include "vec3.h"
#include "sgb.h"
// all credit to
// https://github.com/ufx/SaintCoinach/blob/master/SaintCoinach/Graphics/Lgb/
// this is simply their work ported to c++ since we dont c#
struct LGB_FILE;
struct LGB_FILE_HEADER;
struct LGB_GROUP;
struct LGB_GROUP_HEADER;
enum class LgbEntryType : uint32_t
{
BgParts = 1,
Light = 3,
Vfx = 4,
PositionMarker = 5,
Gimmick = 6,
SharedGroup6 = 6,// secondary variable is set to 2
Sound = 7,
EventNpc = 8,
BattleNpc = 9,
Aetheryte = 12,
EnvSpace = 13,
Gathering = 14,
SharedGroup15 = 15,// secondary variable is set to 13
Treasure = 16,
Weapon = 39,
PopRange = 40,
ExitRange = 41,
MapRange = 43,
NaviMeshRange = 44,
EventObject = 45,
EnvLocation = 47,
EventRange = 49,
QuestMarker = 51,
CollisionBox = 57,
DoorRange = 58,
LineVfx = 59,
ClientPath = 65,
ServerPath = 66,
GimmickRange = 67,
TargetMarker = 68,
ChairMarker = 69,
ClickableRange = 70,
PrefetchRange = 71,
FateRange = 72,
SphereCastRange = 75,
};
class LGB_MODEL_ENTRY
{
public:
char* m_buf;
uint32_t m_offset;
LGB_MODEL_ENTRY()
{
m_buf = nullptr;
m_offset = 0;
};
LGB_MODEL_ENTRY( char* buf, uint32_t offset )
{
m_buf = buf;
m_offset = offset;
};
virtual ~LGB_MODEL_ENTRY() {};
};
struct LGB_BGPARTS_HEADER
{
LgbEntryType type;
uint32_t unknown2;
uint32_t nameOffset;
vec3 translation;
vec3 rotation;
vec3 scale;
uint32_t modelFileOffset;
uint32_t collisionFileOffset;
uint32_t unknown4;
uint32_t unknown5;
uint32_t unknown6;
uint32_t unknown7;
uint32_t unknown8;
uint32_t unknown9;
};
class LGB_BGPARTS_ENTRY : public LGB_MODEL_ENTRY
{
public:
LGB_BGPARTS_HEADER header;
std::string name;
std::string modelFileName;
std::string collisionFileName;
LGB_BGPARTS_ENTRY() {};
LGB_BGPARTS_ENTRY( char* buf, uint32_t offset )
{
header = *reinterpret_cast<LGB_BGPARTS_HEADER*>( buf + offset );
name = std::string( buf + offset + header.nameOffset );
modelFileName = std::string( buf + offset + header.modelFileOffset );
collisionFileName = std::string( buf + offset + header.collisionFileOffset );
//std::cout << "BGPARTS_ENTRY " << name << "\n";
//std::cout << " " << modelFileName << "\n";
//std::cout << " " << collisionFileName << "\n";
};
};
struct LGB_GIMMICK_HEADER
{
LgbEntryType type;
uint32_t unknown;
uint32_t nameOffset;
vec3 translation;
vec3 rotation;
vec3 scale;
uint32_t gimmickFileOffset;
char unknownBytes[100];
};
class LGB_GIMMICK_ENTRY : public LGB_MODEL_ENTRY
{
public:
LGB_GIMMICK_HEADER header;
std::string name;
std::string gimmickFileName;
LGB_GIMMICK_ENTRY( char* buf, uint32_t offset )
{
header = *reinterpret_cast<LGB_GIMMICK_HEADER*>( buf + offset );
name = std::string( buf + offset + header.nameOffset );
gimmickFileName = std::string( buf + offset + header.gimmickFileOffset );
};
};
struct LGB_GROUP_HEADER
{
uint32_t unknown;
int32_t groupNameOffset;
int32_t entriesOffset;
int32_t entryCount;
uint32_t unknown2;
uint32_t unknown3;
uint32_t unknown4;
uint32_t unknown5;
uint32_t unknown6;
uint32_t unknown7;
uint32_t unknown8;
uint32_t unknown9;
uint32_t unknown10;
};
struct LGB_GROUP
{
LGB_FILE* parent;
LGB_GROUP_HEADER header;
std::string name;
std::vector<std::shared_ptr<LGB_MODEL_ENTRY>> entries;
LGB_GROUP( char* buf, LGB_FILE* parentStruct, uint32_t offset )
{
parent = parentStruct;
header = *reinterpret_cast<LGB_GROUP_HEADER*>( buf + offset );
name = std::string( buf + offset + header.groupNameOffset );
//entries.resize( header.entryCount );
//std::cout << name << std::endl;
const auto entriesOffset = offset + header.entriesOffset;
for( auto i = 0; i < header.entryCount; ++i )
{
const auto entryOffset = entriesOffset + *reinterpret_cast<int32_t*>( buf + ( entriesOffset + i * 4 ) );
try
{
const auto type = *reinterpret_cast<LgbEntryType*>( buf + entryOffset );
if( type == LgbEntryType::BgParts )
{
entries.push_back( std::make_shared<LGB_BGPARTS_ENTRY>( buf, entryOffset ) );
}
else if( type == LgbEntryType::Gimmick )
{
entries.push_back( std::make_shared<LGB_GIMMICK_ENTRY>( buf, entryOffset ) );
}
/*
else
{
//entries[i] = nullptr;
}
*/
}
catch( std::exception& e )
{
std::cout << name << " " << e.what() << std::endl;
}
}
};
};
struct LGB_FILE_HEADER
{
char magic[4]; // LGB 1
uint32_t fileSize;
uint32_t unknown;
char magic2[4]; // LGP1
uint32_t unknown2;
uint32_t unknown3;
uint32_t unknown4;
uint32_t unknown5;
int32_t groupCount;
};
struct LGB_FILE
{
LGB_FILE_HEADER header;
std::vector<LGB_GROUP> groups;
LGB_FILE( char* buf )
{
header = *reinterpret_cast<LGB_FILE_HEADER*>( buf );
if( strncmp( &header.magic[0], "LGB1", 4 ) != 0 || strncmp( &header.magic2[0], "LGP1", 4 ) != 0 )
throw std::runtime_error( "Invalid LGB file!" );
//groups.resize(header.groupCount);
constexpr auto baseOffset = sizeof( header );
for( auto i = 0; i < header.groupCount; ++i )
{
const auto groupOffset = baseOffset + *reinterpret_cast<int32_t*>( buf + ( baseOffset + i * 4 ) );
const auto group = LGB_GROUP( buf, this, groupOffset );
groups.push_back( group );
}
};
};
/*
#if __cplusplus >= 201703L
#include <experimental/filesystem>
std::map<std::string, LGB_FILE> getLgbFiles( const std::string& dir )
{
namespace fs = std::experimental::filesystem;
std::map<std::string, LGB_FILE> fileMap;
for( const auto& path : fs::recursive_directory_iterator( dir ) )
{
if( path.path().extension() == ".lgb" )
{
const auto& strPath = path.path().string();
auto f = fopen( strPath.c_str(), "rb" );
fseek( f, 0, SEEK_END );
const auto size = ftell( f );
std::vector<char> bytes( size );
rewind( f );
fread( bytes.data(), 1, size, f );
fclose( f );
try
{
LGB_FILE lgbFile( bytes.data() );
fileMap.insert( std::make_pair( strPath, lgbFile ) );
}
catch( std::exception& e )
{
std::cout << "Unable to load " << strPath << std::endl;
}
}
}
return fileMap;
}
#endif
*/
#endif

View file

@ -1,10 +1,28 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <cstdint>
#include <string>
#include "pcb.h"
#include "lgb.h"
#include "sgb.h"
#include <GameData.h>
#include <File.h>
#include <DatCat.h>
#include <ExdData.h>
#include <ExdCat.h>
#include <Exd.h>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <chrono>
using namespace std::chrono_literals;
struct face
{
int32_t f1, f2, f3;
};
int parseBlockEntry( char* data, std::vector<PCB_BLOCK_ENTRY>& entries, int gOff )
{
@ -14,7 +32,7 @@ int parseBlockEntry( char* data, std::vector<PCB_BLOCK_ENTRY>& entries, int gOff
{
PCB_BLOCK_ENTRY block_entry;
memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) );
isgroup = block_entry.header.type == 0x30 ? true : false;
isgroup = block_entry.header.type == 0x30;
//printf( " BLOCKHEADER_%X: type: %i, group_size: %i\n", gOff + offset, block_entry.header.type, block_entry.header.group_size );
@ -22,12 +40,11 @@ int parseBlockEntry( char* data, std::vector<PCB_BLOCK_ENTRY>& entries, int gOff
{
parseBlockEntry( data + offset + 0x30, entries, gOff + offset );
offset += block_entry.header.group_size;
}
else
{
printf( "\tnum_v16: %i, num_indices: %i, num_vertices: %i\n\n",
block_entry.header.num_v16, block_entry.header.num_indices, block_entry.header.num_vertices );
/* printf( "\tnum_v16: %i, num_indices: %i, num_vertices: %i\n\n",
block_entry.header.num_v16, block_entry.header.num_indices, block_entry.header.num_vertices );*/
int doffset = sizeof( block_entry.header ) + offset;
uint16_t block_size = sizeof( block_entry.header ) +
block_entry.header.num_vertices * 3 * 4 +
@ -52,207 +69,365 @@ int parseBlockEntry( char* data, std::vector<PCB_BLOCK_ENTRY>& entries, int gOff
if( block_entry.header.num_indices != 0 )
{
block_entry.data.indices.resize( block_entry.header.num_indices );
int32_t size_indexbuffer = block_entry.header.num_indices * 6;
int32_t size_indexbuffer = block_entry.header.num_indices * 12;
memcpy( &block_entry.data.indices[0], data + doffset, size_indexbuffer );
doffset += size_indexbuffer;
}
entries.push_back( block_entry );
/* printf( "Vertices: \n" );
for( auto& entry1 : block_entry.data.vertices )
{
printf( "\t %f, %f, %f \n",
entry1.x, entry1.y, entry1.z );
}
float x_base = abs( float( block_entry.header.x1 - block_entry.header.x ) );
float y_base = abs( float( block_entry.header.y1 - block_entry.header.y ) );
float z_base = abs( float( block_entry.header.z1 - block_entry.header.z ) );
printf( "Vertices I16: \n" );
for( auto& entry1 : block_entry.data.vertices_i16 )
{
uint16_t var1 = entry1.x;
uint16_t var2 = entry1.y;
uint16_t var3 = entry1.z;
float x = ( var1 );
float y = ( var2 );
float z = ( var3 );
printf( "\t%f, ", ( x / 0xFFFF ) * x_base + block_entry.header.x );
printf( "%f, ", ( y / 0xFFFF ) * y_base + block_entry.header.y );
printf( "%f ", ( z / 0xFFFF ) * z_base + block_entry.header.z );
printf( "\n" );
}*/
}
}
return 0;
}
int main()
std::string zoneNameToPath( const std::string& name )
{
char *data;
uint32_t offset = 0;
//r1f1_b1_dor00.pcb
//std::string filename( "f1h0_s_rof0003.pcb" );
std::string filename( "tr0924.pcb" );
FILE *fp = nullptr;
fp = fopen( filename.c_str(), "rb" );
if( fp == nullptr )
char teri = name[0];
char region = name[1];
char type = name[2];
char zone = name[3];
static std::map<char, std::string> teriMap
{
return 0;
}
{ 'r', "roc" },
{ 'w', "wil" },
{ 'l', "lak" },
{ 'o', "ocn" },
{ 'f', "fst" },
{ 'a', "air" },
{ 's', "sea" },
{ 'z', "zon" }
};
fseek( fp, 0, SEEK_END );
int32_t size = ftell( fp );
data = new char[size];
rewind( fp );
fread( data, sizeof( char ), size, fp );
fclose( fp );
PCB_FILE pcb_file;
memcpy( &pcb_file.header, data, sizeof( pcb_file.header ) );
offset += sizeof( pcb_file.header );
std::vector<PCB_BLOCK_ENTRY> entries;
bool isgroup = true;
while( isgroup )
static std::map<char, std::string> typeMap
{
PCB_BLOCK_ENTRY block_entry;
memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) );
isgroup = block_entry.header.type == 0x30 ? true : false;
{ 'f', "fld" },
{ 't', "twn" },
{ 'd', "dun" },
{ 'b', "bah" },
{ 'i', "ind" },
{ 'e', "evt" },
};
std::string ret;
const auto& teriRet = teriMap[teri];
const auto& typeRet = typeMap[type];
ret += teriRet + "_";
ret += teri;
ret += region;
ret += "/" + typeRet + "/" + name;
return ret;
}
//printf( "BLOCKHEADER_%X: type: %i, group_size: %i\n", offset, block_entry.header.type, block_entry.header.group_size );
int main( int argc, char* argv[] )
{
auto startTime = std::chrono::system_clock::now();
if( isgroup )
std::string gamePath = "C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv";
std::string zoneName = "r1f1";
if( argc > 1 )
{
gamePath = argv[1];
if( argc > 2 )
{
std::vector<uint8_t> data_block( block_entry.header.group_size );
memcpy( &data_block[0], data + offset, block_entry.header.group_size );
parseBlockEntry( (char*)&data_block[0] + 0x30, entries, offset );
offset += block_entry.header.group_size;
zoneName = argv[2];
}
}
const auto& zonePath = zoneNameToPath( zoneName );
try
{
xiv::dat::GameData data1( gamePath );
xiv::exd::ExdData eData( data1 );
const xiv::dat::Cat& test = data1.get_category( "bg" );
auto test_file = data1.get_file( "bg/ffxiv/" + zonePath + "/level/bg.lgb" );
auto section = test_file->access_data_sections().at( 0 );
int32_t list_offset = *(uint32_t*)&section[0x18];
int32_t size = *(uint32_t*)&section[4];
std::vector<std::string> stringList;
auto test_file1 = data1.get_file( "bg/ffxiv/" + zonePath + "/collision/list.pcb" );
auto section1 = test_file1->access_data_sections().at( 0 );
std::string path = "bg/ffxiv/" + zonePath + "/collision/";
uint32_t offset1 = 0x20;
for( ; ; )
{
uint16_t trId = *(uint16_t*)&section1[offset1];
char someString[200];
sprintf( someString, "%str%04d.pcb", path.c_str(), trId );
stringList.push_back( std::string( someString ) );
//std::cout << someString << "\n";
offset1 += 0x20;
if( offset1 >= section1.size() )
{
break;
}
}
LGB_FILE bgLgb( &section[0] );
uint32_t max_index = 0;
// dont bother if we cant write to a file
auto fp_out = fopen( ( zoneName + ".obj" ).c_str(), "w" );
if( fp_out )
{
fprintf( fp_out, "\n" );
fclose( fp_out );
}
else
{
parseBlockEntry( data + offset, entries, offset );
std::string errorMessage( "Cannot create " + zoneName + ".obj\n" +
" Check no programs have a handle to file and run as admin.\n" );
std::cout << errorMessage;
throw std::runtime_error( errorMessage.c_str() );
return 0;
}
fp_out = fopen( ( zoneName + ".obj" ).c_str(), "ab+" );
if( fp_out )
{
std::map<std::string, PCB_FILE> pcbFiles;
std::map<std::string, SGB_FILE> sgbFiles;
std::map<std::string, uint32_t> objCount;
auto loadPcbFile = [&]( const std::string& fileName ) -> bool
{
try
{
//std::cout << fileName << " ";
auto file = data1.get_file( fileName );
auto sections = file->get_data_sections();
auto dataSection = &sections.at( 0 )[0];
//std::cout << sections.size() << "\n";
uint32_t offset = 0;
PCB_FILE pcb_file;
memcpy( &pcb_file.header, &dataSection[0], sizeof( pcb_file.header ) );
offset += sizeof( pcb_file.header );
pcb_file.entries.resize( pcb_file.header.num_entries );
bool isgroup = true;
while( isgroup )
{
PCB_BLOCK_ENTRY block_entry;
memcpy( &block_entry.header, &dataSection[0] + offset, sizeof( block_entry.header ) );
isgroup = block_entry.header.type == 0x30;
//printf( "BLOCKHEADER_%X: type: %i, group_size: %i\n", offset, block_entry.header.type, block_entry.header.group_size );
//
if( isgroup )
{
parseBlockEntry( &dataSection[0] + offset + 0x30, pcb_file.entries, offset );
offset += block_entry.header.group_size;
}
else
{
parseBlockEntry( &dataSection[0] + offset, pcb_file.entries, offset );
}
}
pcbFiles.insert( std::make_pair( fileName, pcb_file ) );
return true;
}
catch( std::exception& e )
{
std::cout << "Unable to load collision mesh " << fileName << "\n\tError:\n\t" << e.what() << "\n";
return false;
}
};
auto loadSgbFile = [&]( const std::string& fileName ) -> bool
{
SGB_FILE sgbFile;
try
{
auto file = data1.get_file( fileName );
auto sections = file->get_data_sections();
auto dataSection = &sections.at( 0 )[0];
sgbFile = SGB_FILE( &dataSection[0] );
sgbFiles.insert( std::make_pair( fileName, sgbFile ) );
return true;
}
catch( std::exception& e )
{
std::cout << "Unable to load SGB " << fileName << "\n\tError:\n\t" << e.what() << "\n";
sgbFiles.insert( std::make_pair( fileName, sgbFile ) );
}
return false;
};
auto pushVerts = [&]( const PCB_FILE& pcb_file, const std::string& name,
const vec3* scale = nullptr,
const vec3* rotation = nullptr,
const vec3* translation = nullptr,
const SGB_MODEL_ENTRY* pSgbEntry = nullptr)
{
char name2[0x100];
memset( name2, 0, 0x100 );
sprintf( &name2[0], "%s_%u", &name[0], objCount[name]++ );
fprintf( fp_out, "o %s\n", &name2[0] );
uint32_t groupCount = 0;
for( const auto &entry : pcb_file.entries )
{
float x_base = abs( float( entry.header.x1 - entry.header.x ) );
float y_base = abs( float( entry.header.y1 - entry.header.y ) );
float z_base = abs( float( entry.header.z1 - entry.header.z ) );
auto makeTranslation = [&]( vec3& v )
{
if( pSgbEntry )
{
v.x *= pSgbEntry->header.scale.x;
v.y *= pSgbEntry->header.scale.y;
v.z *= pSgbEntry->header.scale.z;
v = v * matrix4::rotateX( pSgbEntry->header.rotation.x );
v = v * matrix4::rotateY( pSgbEntry->header.rotation.y );
v = v * matrix4::rotateZ( pSgbEntry->header.rotation.z );
v.x += pSgbEntry->header.translation.x;
v.y += pSgbEntry->header.translation.y;
v.z += pSgbEntry->header.translation.z;
}
if( scale )
{
v.x *= scale->x;
v.y *= scale->y;
v.z *= scale->z;
v = v * matrix4::rotateX( rotation->x );
v = v * matrix4::rotateY( rotation->y );
v = v * matrix4::rotateZ( rotation->z );
v.x += translation->x;
v.y += translation->y;
v.z += translation->z;
}
};
for( auto &vertex : entry.data.vertices )
{
vec3 v( vertex.x, vertex.y, vertex.z );
makeTranslation( v );
fprintf( fp_out, "v %f %f %f\n", v.x, v.y, v.z );
}
for( const auto &link : entry.data.vertices_i16 )
{
vec3 v( float( link.x ) / 0xFFFF, float( link.y ) / 0xFFFF, float( link.z ) / 0xFFFF );
v.x = v.x * x_base + entry.header.x;
v.y = v.y * y_base + entry.header.y;
v.z = v.z * z_base + entry.header.z;
makeTranslation( v );
fprintf( fp_out, "v %f %f %f\n", v.x, v.y, v.z );
}
//fprintf( fp_out, "g %s_", (name2 + "_" + std::to_string( groupCount++ )).c_str() );
for( const auto &index : entry.data.indices )
{
fprintf( fp_out, "f %i %i %i\n",
index.index[0] + max_index + 1,
index.index[1] + max_index + 1,
index.index[2] + max_index + 1 );
// std::cout << std::to_string( index.unknown[0] )<< " " << std::to_string( index.unknown[1] )<< " " << std::to_string( index.unknown[2]) << std::endl;
}
max_index += entry.data.vertices.size() + entry.data.vertices_i16.size();
}
};
for( const auto& fileName : stringList )
{
loadPcbFile( fileName );
pushVerts( pcbFiles[fileName], fileName );
}
std::cout << "Writing obj file " << "\n";
std::cout << bgLgb.groups.size() << " groups " << "\n";
uint32_t totalGroups = 0;
uint32_t totalGroupEntries = 0;
for( const auto& group : bgLgb.groups )
{
//std::cout << "\t" << group.name << " Size " << group.header.entryCount << "\n";
totalGroups++;
for( const auto& pEntry : group.entries )
{
auto pGimmick = dynamic_cast<LGB_GIMMICK_ENTRY*>( pEntry.get() );
auto pBgParts = dynamic_cast<LGB_BGPARTS_ENTRY*>( pEntry.get() );
std::string fileName( "" );
fileName.resize( 256 );
totalGroupEntries++;
// write files
auto writeOutput = [&]( const std::string& fileName, const vec3* scale, const vec3* rotation, const vec3* translation, const SGB_MODEL_ENTRY* pModel = nullptr) -> bool
{
{
const auto& it = pcbFiles.find( fileName );
if( it == pcbFiles.end() )
{
if( fileName.empty() || !loadPcbFile( fileName ) )
return false;
//std::cout << "\t\tLoaded PCB File " << pBgParts->collisionFileName << "\n";
}
}
const auto& it = pcbFiles.find( fileName );
if( it != pcbFiles.end() )
{
const auto& pcb_file = it->second;
pushVerts( pcb_file, fileName, scale, rotation, translation, pModel );
}
return true;
};
if( pBgParts )
{
fileName = pBgParts->collisionFileName;
writeOutput( fileName, &pBgParts->header.scale, &pBgParts->header.rotation, &pBgParts->header.translation );
}
// gimmick entry
if( pGimmick )
{
{
const auto& it = sgbFiles.find( pGimmick->gimmickFileName );
if( it == sgbFiles.end() )
{
//std::cout << "\tGIMMICK:\n\t\t" << pGimmick->name << " " << pGimmick->gimmickFileName << "\n";
loadSgbFile( pGimmick->gimmickFileName );
}
}
const auto& it = sgbFiles.find( pGimmick->gimmickFileName );
if( it != sgbFiles.end() )
{
const auto& sgbFile = it->second;
for( const auto& group : sgbFile.entries )
{
for( const auto& pEntry : group.entries )
{
auto pModel = dynamic_cast<SGB_MODEL_ENTRY*>( pEntry.get() );
fileName = pModel->collisionFileName;
writeOutput( fileName, &pGimmick->header.scale, &pGimmick->header.rotation, &pGimmick->header.translation, pModel );
}
}
}
}
}
}
std::cout << "\n\nLoaded " << pcbFiles.size() << " PCB Files \n";
std::cout << "Total Groups " << totalGroups << " Total entries " << totalGroupEntries << "\n";
}
std::cout << "Finished exporting " << zoneName << " in " <<
std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now() - startTime ).count() << " seconds\n";
}
for( uint16_t i = 0; i <= pcb_file.header.num_entries; i++ )
catch( std::exception& e )
{
PCB_BLOCK_ENTRY block_entry;
memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) );
offset += sizeof( block_entry.header );
uint16_t block_size = sizeof( block_entry.header ) +
block_entry.header.num_vertices * 3 * 4 +
block_entry.header.num_v16 * 6 +
block_entry.header.num_indices * 6;
if( block_entry.header.num_vertices != 0 )
{
block_entry.data.vertices.resize( block_entry.header.num_vertices );
int32_t size_vertexbuffer = block_entry.header.num_vertices * 3;
memcpy( &block_entry.data.vertices[0], data + offset, size_vertexbuffer * 4 );
offset += size_vertexbuffer * 4;
}
if( block_entry.header.num_v16 != 0 )
{
block_entry.data.vertices_i16.resize( block_entry.header.num_v16 );
int32_t size_unknownbuffer = block_entry.header.num_v16 * 6;
memcpy( &block_entry.data.vertices_i16[0], data + offset, size_unknownbuffer );
offset += block_entry.header.num_v16 * 6;
}
if( block_entry.header.num_indices != 0 )
{
block_entry.data.indices.resize( block_entry.header.num_indices );
int32_t size_indexbuffer = block_entry.header.num_indices * 6;
memcpy( &block_entry.data.indices[0], data + offset, size_indexbuffer );
offset += size_indexbuffer;
}
// blocks always align to 16 bytes + 8 bytes padding till the next block
int rest = ( offset % 16 );
if( rest > 0 )
{
rest = 0x10 - rest;
}
offset += rest ;
pcb_file.entries.push_back( block_entry );
std::cout << e.what() << std::endl;
}
FILE* fp_out1 = fopen( std::string( filename + ".plain" ).c_str(), "w" );
fprintf( fp_out1, "");
fclose( fp_out1 );
FILE* fp_out = fopen( std::string( filename + ".plain" ).c_str(), "w+" );
fprintf( fp_out, "HEADER: num_entries: %i, total_indices: %i, unknown_1: %i\n\n", pcb_file.header.num_entries, pcb_file.header.total_indices, pcb_file.header.unknown_1 );
int block_cnt = 0;
for( auto& entry : pcb_file.entries )
{
fprintf( fp_out, "BLOCKHEADER_%i: type: %i, group_size: %i\n ",
block_cnt, entry.header.type, entry.header.group_size );
fprintf( fp_out, "\tAABB: x: %f, y: %f, z: %f\n ",
entry.header.x, entry.header.y, entry.header.z );
fprintf( fp_out, "\t\t x1: %f, y1: %f, z1: %f\n ",
entry.header.x1, entry.header.y1, entry.header.z1 );
fprintf( fp_out, "\tnum_v16: %i, num_indices: %i, num_vertices: %i\n\n",
entry.header.num_v16, entry.header.num_indices, entry.header.num_vertices );
fprintf( fp_out, "Vertices: \n");
for( auto& entry1 : entry.data.vertices )
{
fprintf( fp_out, "\t %f, %f, %f \n",
entry1.x, entry1.y, entry1.z );
}
float x_base = abs( float( entry.header.x1 - entry.header.x ) );
float y_base = abs( float( entry.header.y1 - entry.header.y ) );
float z_base = abs( float( entry.header.z1 - entry.header.z ) );
fprintf( fp_out, "Vertices I16: \n" );
for( auto& entry1 : entry.data.vertices_i16 )
{
uint16_t var1 = entry1.x;
uint16_t var2 = entry1.y;
uint16_t var3 = entry1.z;
float x = ( var1 );
float y = ( var2 );
float z = ( var3 );
fprintf( fp_out, "\t%f, ", (x / 0xFFFF) * x_base + entry.header.x );
fprintf( fp_out, "%f, ", (y / 0xFFFF) * y_base + entry.header.y );
fprintf( fp_out, "%f ", (z / 0xFFFF) * z_base + entry.header.z );
fprintf( fp_out, "\n");
}
fprintf( fp_out, "Indices: \n" );
for( auto& entry1 : entry.data.indices )
{
fprintf( fp_out, "\t %i, %i, %i - %x,%x,%x \n",
entry1.index[0], entry1.index[1], entry1.index[2], entry1.unknown[0], entry1.unknown[1], entry1.unknown[2] );
}
fprintf( fp_out, "\n" );
}
fclose( fp_out );
return 0;
}

View file

@ -0,0 +1,100 @@
#ifndef _MATRIX4_H
#define _MATRIX4_H
#include <cstdint>
#include <cmath>
// https://github.com/jpd002/Play--Framework/tree/master/include/math
struct matrix4
{
// 4x4
float grid[16];
matrix4()
{
memset( &grid[0], 0, sizeof( grid ) );
}
float operator()( int row, int col ) const
{
return grid[(row * 4) + col];
}
float& operator()( int row, int col )
{
return grid[(row * 4) + col];
}
static matrix4 rotateX( float angle )
{
matrix4 ret = matrix4();
ret(0, 0) = 1.000000000f;
ret(1, 1) = cos(angle);
ret(1, 2) = -sin(angle);
ret(2, 1) = sin(angle);
ret(2, 2) = cos(angle);
ret(3, 3) = 1.000000000f;
return ret;
}
static matrix4 rotateY( float angle )
{
matrix4 ret = matrix4();
ret(0, 0) = cos(angle);
ret(0, 2) = sin(angle);
ret(1, 1) = 1.000000000f;
ret(2, 0) = -sin(angle);
ret(2, 2) = cos(angle);
ret(3, 3) = 1.000000000f;
return ret;
}
static matrix4 rotateZ( float angle )
{
matrix4 ret = matrix4();
ret(0, 0) = cos(angle);
ret(0, 1) = -sin(angle);
ret(1, 0) = sin(angle);
ret(1, 1) = cos(angle);
ret(2, 2) = 1.000000000f;
ret(3, 3) = 1.000000000f;
return ret;
}
static matrix4 scale( float x, float y, float z )
{
matrix4 ret = matrix4();
ret(0, 0) = x;
ret(1, 1) = y;
ret(2, 2) = z;
ret(3, 3) = 1;
return ret;
}
static matrix4 translate( float x, float y, float z )
{
matrix4 ret = matrix4();
ret(0, 0) = 1;
ret(1, 1) = 1;
ret(2, 2) = 1;
ret(3, 3) = 1;
ret(3, 0) = x;
ret(3, 1) = y;
ret(3, 2) = z;
return ret;
}
matrix4 operator *( const matrix4& rhs ) const
{
matrix4 ret;
for( unsigned int i = 0; i < 4; i++ )
{
ret( i, 0 ) = (*this)(i, 0) * rhs( 0, 0 ) + (*this)(i, 1) * rhs( 1, 0 ) + (*this)(i, 2) * rhs( 2, 0 ) + (*this)(i, 3) * rhs( 3, 0 );
ret( i, 1 ) = (*this)(i, 0) * rhs( 0, 1 ) + (*this)(i, 1) * rhs( 1, 1 ) + (*this)(i, 2) * rhs( 2, 1 ) + (*this)(i, 3) * rhs( 3, 1 );
ret( i, 2 ) = (*this)(i, 0) * rhs( 0, 2 ) + (*this)(i, 1) * rhs( 1, 2 ) + (*this)(i, 2) * rhs( 2, 2 ) + (*this)(i, 3) * rhs( 3, 2 );
ret( i, 3 ) = (*this)(i, 0) * rhs( 0, 3 ) + (*this)(i, 1) * rhs( 1, 3 ) + (*this)(i, 2) * rhs( 2, 3 ) + (*this)(i, 3) * rhs( 3, 3 );
}
return ret;
}
};
#endif

View file

@ -1,3 +1,6 @@
#ifndef _PCB_H
#define _PCB_H
#include <stdint.h>
#include <vector>
@ -40,6 +43,7 @@ struct PCB_INDEXDATA
{
uint8_t index[3];
uint8_t unknown[3];
uint8_t unknown1[6];
};
struct PCB_VERTEXDATAI16
@ -67,3 +71,22 @@ struct PCB_FILE
PCB_HEADER header;
std::vector< PCB_BLOCK_ENTRY > entries;
};
struct PCB_LIST_ENTRY
{
uint32_t id;
float x, y, z, x2, y2, z2, rot;
};
struct PCB_LIST_BASE_ENTRY
{
float x, y, z, x2, y2, z2, rot;
};
struct PCB_LIST_FILE
{
uint32_t count;
PCB_LIST_BASE_ENTRY entry;
std::vector<PCB_LIST_ENTRY> entries;
};
#endif

202
src/tools/pcb_reader/sgb.h Normal file
View file

@ -0,0 +1,202 @@
#ifndef _SGB_H
#define _SGB_H
#include <cstring>
#include <memory>
#include <cstdint>
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include "vec3.h"
//
// ported from https://github.com/ufx/SaintCoinach/blob/master/SaintCoinach/Graphics/Sgb/SgbDataType.cs
struct SGB_FILE;
struct SGB_HEADER;
struct SGB_MODEL_ENTRY;
struct SGB_MODEL_HEADER;
struct SGB_GROUP;
struct SGB_GROUP_HEADER;
enum SgbDataType : uint32_t
{
Unknown0008 = 0x0008,
Group = 0x0100,
};
enum SgbGroupEntryType : uint32_t
{
Model = 0x01,
};
struct SGB_GROUP_HEADER
{
SgbDataType type;
int32_t nameOffset;
uint32_t unknown08;
uint32_t unknown0C;
uint32_t unknown10;
uint32_t unknown14;
uint32_t unknown18;
uint32_t unknown1C;
int32_t entryCount;
uint32_t unknown24;
uint32_t unknown28;
uint32_t unknown2C;
uint32_t unknown30;
uint32_t unknown34;
uint32_t unknown38;
uint32_t unknown3C;
uint32_t unknown40;
uint32_t unknown44;
};
struct SGB_GROUP_ENTRY
{
public:
char* m_buf;
uint32_t m_offset;
SGB_GROUP_ENTRY()
{
m_buf = nullptr;
m_offset = 0;
};
SGB_GROUP_ENTRY( char* buf, uint32_t offset )
{
m_buf = buf;
m_offset = offset;
};
virtual ~SGB_GROUP_ENTRY() {};
};
struct SGB_MODEL_HEADER
{
SgbGroupEntryType type;
uint32_t unknown2;
int32_t nameOffset;
vec3 translation;
vec3 rotation;
vec3 scale;
int32_t modelFileOffset;
int32_t collisionFileOffset;
};
struct SGB_MODEL_ENTRY : public SGB_GROUP_ENTRY
{
SGB_MODEL_HEADER header;
SgbGroupEntryType type;
std::string name;
std::string modelFileName;
std::string collisionFileName;
SGB_MODEL_ENTRY( char* buf, uint32_t offset )
{
header = *reinterpret_cast<SGB_MODEL_HEADER*>( buf + offset );
name = std::string( buf + offset + header.nameOffset );
modelFileName = std::string( buf + offset + header.modelFileOffset );
collisionFileName = std::string( buf + offset + header.collisionFileOffset );
}
};
struct SGB_GROUP
{
SGB_GROUP_HEADER header;
std::string name;
SGB_FILE* parent;
std::vector<std::shared_ptr<SGB_GROUP_ENTRY>> entries;
SGB_GROUP( char* buf, SGB_FILE* file, uint32_t fileSize, uint32_t offset )
{
parent = file;
header = *reinterpret_cast<SGB_GROUP_HEADER*>( buf + offset );
name = std::string( buf + offset + header.nameOffset );
auto entriesOffset = offset + sizeof( header );
for( auto i = 0; i < header.entryCount; ++i )
{
auto entryOffset = entriesOffset + *reinterpret_cast<uint32_t*>( buf + ( entriesOffset + (i * 4) ) );
if( entryOffset > fileSize )
throw std::runtime_error( "SGB_GROUP entry offset was larger than SGB file size!" );
auto type = *reinterpret_cast<uint32_t*>( buf + entryOffset );
if( type == SgbGroupEntryType::Model )
{
entries.push_back( std::make_shared<SGB_MODEL_ENTRY>( buf, entryOffset ) );
}
}
}
};
struct SGB_HEADER
{
char magic[4]; // SGB1
uint32_t fileSize;
uint32_t unknown1;
char magic2[4]; // SCN1
uint32_t unknown10;
int32_t sharedOffset;
uint32_t unknown18;
int32_t offset1C;
uint32_t unknown20;
uint32_t unknown24;
uint32_t unknown28;
uint32_t unknown2C;
uint32_t unknown30;
uint32_t unknown34;
uint32_t unknown38;
uint32_t unknown3C;
uint32_t unknown40;
uint32_t unknown44;
uint32_t unknown48;
uint32_t unknown4C;
uint32_t unknown50;
uint32_t unknown54;
};
struct SGB_FILE
{
SGB_HEADER header;
std::vector<SGB_GROUP> entries;
SGB_FILE()
{
memset( &header, 0, sizeof( header ) );
}
SGB_FILE( char* buf )
{
constexpr int baseOffset = 0x14;
header = *reinterpret_cast<SGB_HEADER*>( buf );
if( strncmp( &header.magic[0], "SGB1", 4 ) != 0 || strncmp( &header.magic2[0], "SCN1", 4 ) != 0 )
throw std::runtime_error( "Unable to load SGB File!" );
try
{
auto group = SGB_GROUP( buf, this, header.fileSize, baseOffset + header.sharedOffset );
entries.push_back( group );
auto group2 = SGB_GROUP( buf, this, header.fileSize, baseOffset + header.offset1C );
entries.push_back( group2 );
}
catch( std::exception& e )
{
std::cout << e.what() << "\n";
}
};
};
#endif // !_SGB_H

View file

@ -0,0 +1,31 @@
#ifndef _VEC3_H
#define _VEC3_H
#include <cstdint>
#include "matrix4.h"
struct vec3
{
float x, y, z;
vec3()
{
x = 0.0f;
y = 0.0f;
z = 0.0f;
}
vec3(float x, float y, float z)
{
this->x = x;
this->y = y;
this->z = z;
};
};
static vec3 operator *(const vec3& lhs, const matrix4& rhs)
{
vec3 ret;
ret.x = rhs(0, 0) * lhs.x + rhs(0, 1) * lhs.y + rhs(0, 2) * lhs.z;
ret.y = rhs(1, 0) * lhs.x + rhs(1, 1) * lhs.y + rhs(1, 2) * lhs.z;
ret.z = rhs(2, 0) * lhs.x + rhs(2, 1) * lhs.y + rhs(2, 2) * lhs.z;
return ret;
};
#endif

View file

@ -91,7 +91,7 @@ void createScript( boost::shared_ptr< Core::Data::QuestInfo > pQuestData, std::s
if( pQuestData->reward_item.size() > 0 )
{
rewards += " this.RewardItem = [";
for( int ca = 0; ca < pQuestData->reward_item.size(); ca++ )
for( size_t ca = 0; ca < pQuestData->reward_item.size(); ca++ )
{
rewards += std::to_string( pQuestData->reward_item.at( ca ) );
if( ca != pQuestData->reward_item.size() - 1 )
@ -105,7 +105,7 @@ void createScript( boost::shared_ptr< Core::Data::QuestInfo > pQuestData, std::s
if( pQuestData->reward_item.size() > 0 )
{
rewards += " this.RewardItemCount = [";
for( int ca = 0; ca < pQuestData->reward_item_count.size(); ca++ )
for( size_t ca = 0; ca < pQuestData->reward_item_count.size(); ca++ )
{
rewards += std::to_string( pQuestData->reward_item_count.at( ca ) );
if( ca != pQuestData->reward_item_count.size() - 1 )
@ -119,7 +119,7 @@ void createScript( boost::shared_ptr< Core::Data::QuestInfo > pQuestData, std::s
if( pQuestData->reward_item_optional.size() > 0 )
{
rewards += " this.RewardItemOptional = [";
for( int ca = 0; ca < pQuestData->reward_item_optional.size(); ca++ )
for( size_t ca = 0; ca < pQuestData->reward_item_optional.size(); ca++ )
{
rewards += std::to_string( pQuestData->reward_item_optional.at( ca ) );
if( ca != pQuestData->reward_item_optional.size() - 1 )
@ -133,7 +133,7 @@ void createScript( boost::shared_ptr< Core::Data::QuestInfo > pQuestData, std::s
if( pQuestData->reward_item_optional_count.size() > 0 )
{
rewards += " this.RewardItemOptionalCount = [";
for( int ca = 0; ca < pQuestData->reward_item_optional_count.size(); ca++ )
for( size_t ca = 0; ca < pQuestData->reward_item_optional_count.size(); ca++ )
{
rewards += std::to_string( pQuestData->reward_item_optional_count.at( ca ) );
if( ca != pQuestData->reward_item_optional_count.size() - 1 )
@ -151,7 +151,7 @@ void createScript( boost::shared_ptr< Core::Data::QuestInfo > pQuestData, std::s
std::vector< std::string > script_entities;
std::string sentities = " // Entities found in the script data of the quest\n";
for( int ca = 0; ca < pQuestData->script_entity.size(); ca ++ )
for( size_t ca = 0; ca < pQuestData->script_entity.size(); ca ++ )
{
if( ( pQuestData->script_entity.at( ca ).find( "HOWTO" ) != std::string::npos ) || ( pQuestData->script_entity.at( ca ).find( "HOW_TO" ) != std::string::npos ) )
continue;