uox3/source/books.cpp
Xoduz 37a76083de Rest of the changes for 0.99.6-RC1
Misc cleanup and standardization of style and naming conventions in code and scripts
Added missing code for showing race in paperdoll based on ini setting
Players can no longer use Polymorph spell while incognitoed, or while under effect of tribal paint
Players can no longer use Incognito spell while polymorphed, or while under the effects of tribal paint
Facial hair is now removed from players who are under the effects of Incognito spell that changes their gender for female
2022-10-24 18:39:33 +08:00

465 lines
13 KiB
C++

//o------------------------------------------------------------------------------------------------o
//| File - books.cpp
//| Date - 12/05/1999
//o------------------------------------------------------------------------------------------------o
//| Purpose - Writable and Pre-Defined Book Handling
//o------------------------------------------------------------------------------------------------o
//| Changes - Version History
//|
//| 0.9 12/05/1999
//| Initial implimentation
//|
//| 1.0 12/07/1999
//| Wrapped everything in a class, added customizable number of max-pages
//|
//| 1.1 12/12/1999
//| Nasty bug fixed (item # used as "key" instead of serial #) and a few very small bugfixes
//|
//| 2.0 5th November, 2005
//| Rewrote much of the original .bok handling to improve performance
//| Fixed several issues with the fstream handling to ensure validity of data from .bok files.
//| Slimmed down the cBook class making use of packet classes for handling data from the client.
//| Added support for the new book header packet removing support for the old book header packet.
//o------------------------------------------------------------------------------------------------o
#include "uox3.h"
#include "books.h"
#include "cServerDefinitions.h"
#include "ssection.h"
#include "CPacketSend.h"
#include "StringUtility.hpp"
#include "osunique.hpp"
using namespace std::string_literals;
CBooks *Books = nullptr;
//o------------------------------------------------------------------------------------------------o
//| Function - CPINewBookHeader::Handle()
//| Date - 11/5/2005
//o------------------------------------------------------------------------------------------------o
//| Purpose - Updates the .bok file with changes to the author or title
//o------------------------------------------------------------------------------------------------o
bool CPINewBookHeader::Handle( void )
{
if( tSock != nullptr )
{
const SERIAL bookSer = tSock->GetDWord( 3 );
CItem *mBook = CalcItemObjFromSer( bookSer );
if( !ValidateObject( mBook ))
return true;
const std::string fileName = cwmWorldState->ServerData()->Directory( CSDDP_BOOKS ) + oldstrutil::number( bookSer, 16 ) + std::string( ".bok" );
if( !FileExists( fileName ))
{
Books->CreateBook( fileName, tSock->CurrcharObj(), mBook );
}
std::fstream file( fileName.c_str(), std::ios::in | std::ios::out | std::ios::binary );
if( file.is_open() ) // If it isn't open now, our create and open failed
{
const size_t titleLen = tSock->GetWord( 11 );
const size_t authorLen = tSock->GetWord( 13 + titleLen );
char titleBuff[62];
char authBuff[32];
memset( titleBuff, 0x00, 62 );
memset( authBuff, 0x00, 32 );
memcpy( titleBuff, &tSock->Buffer()[13], ( titleLen > 61 ? 61 : titleLen ));
memcpy( authBuff, &tSock->Buffer()[15 + titleLen], ( authorLen > 31 ? 31 : authorLen ));
mBook->SetName( titleBuff );
file.seekp( 0, std::ios::beg );
if( !file.fail() )
{
file.write( titleBuff, 62 );
file.write( authBuff, 32 );
if( file.fail() )
{
Console.Error( oldstrutil::format( "Couldn't write to book file %s", fileName.c_str() ));
}
}
else
{
Console.Error( oldstrutil::format( "Failed to seek to book file %s", fileName.c_str() ));
}
file.close();
}
else
{
Console.Error( oldstrutil::format( "Couldn't write to book file %s for book 0x%X", fileName.c_str(), bookSer) );
}
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CBooks::OpenPreDefBook()
//| Date - 11/5/2005
//o------------------------------------------------------------------------------------------------o
//| Purpose - Opens "Read Only" Books defined in /misc/books.dfn
//o------------------------------------------------------------------------------------------------o
auto CBooks::OpenPreDefBook( CSocket *mSock, CItem *i ) -> void
{
if( mSock )
{
auto temp = "BOOK "s + oldstrutil::number( i->GetTempVar( CITV_MORE ));
auto book = FileLookup->FindEntry( temp, misc_def );
if( book )
{
CPNewBookHeader toSend;
toSend.Serial( i->GetSerial() );
toSend.Flag1( 0 );
toSend.Flag2( 0 );
bool part1 = false, part2 = false, part3 = false;
for( const auto &sec : book->collection() )
{
auto tag = sec->tag;
auto UTag = oldstrutil::upper( tag );
auto data = sec->data;
if( UTag == "PAGES" )
{
part1 = true;
toSend.Pages( static_cast<UI16>( std::stoul( data, nullptr, 0 )));
}
else if( UTag == "TITLE" )
{
part2 = true;
toSend.Title( data );
}
else if( UTag == "AUTHOR" )
{
part3 = true;
toSend.Author( data );
}
if( part1 && part2 && part3 )
{
break;
}
}
toSend.Finalize();
mSock->Send( &toSend );
}
ReadPreDefBook( mSock, i, 1 );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CBooks::OpenBook()
//| Date - 11/5/2005
//o------------------------------------------------------------------------------------------------o
//| Purpose - Opens Writable books based on their .bok file
//o------------------------------------------------------------------------------------------------o
void CBooks::OpenBook( CSocket *mSock, CItem *mBook, bool isWriteable )
{
if( mSock != nullptr )
{
CPBookPage cpbpToSend;
CPNewBookHeader bInfo;
bInfo.Serial( mBook->GetSerial() );
UI16 numPages = 0;
std::string bookTitle, bookAuthor;
const std::string fileName = cwmWorldState->ServerData()->Directory( CSDDP_BOOKS ) + oldstrutil::number( mBook->GetSerial(), 16 ) + std::string( ".bok" );
std::ifstream file( fileName.c_str(), std::ios::in | std::ios::binary );
bool bookExists = ( file.is_open() );
if( bookExists )
{
file.seekg( 0, std::ios::beg );
if( !file.fail() )
{
char rBuffer[2];
char tempString[62];
file.read( tempString, 62 );
bookTitle = tempString;
file.read( tempString, 32 );
bookAuthor = tempString;
file.read( rBuffer, 2 );
numPages = static_cast<UI16>(( rBuffer[0] << 8 ) + rBuffer[1]);
cpbpToSend.Serial( mBook->GetSerial() );
for( UI16 pageNum = 0; pageNum < numPages; ++pageNum )
{
UI08 blankLineCtr = 0;
std::vector<std::string> tempLines;
tempLines.resize( 0 );
for( UI08 lineNum = 0; lineNum < 8; ++lineNum )
{
file.read( tempString, 34 );
tempLines.push_back( tempString );
if( tempString[0] == 0x00 )
{
++blankLineCtr;
}
}
if( blankLineCtr == 8 )
{
tempLines.clear();
}
cpbpToSend.NewPage( -1, &tempLines );
}
cpbpToSend.Finalize();
}
else
{
Console.Error( oldstrutil::format( "Failed to seek to book file %s", fileName.c_str() ));
}
file.close();
}
else
{
numPages = static_cast<UI16>( mBook->GetTempVar( CITV_MOREY )); // if new book get number of maxpages ...
if( numPages < 1 || numPages > 255 )
{
numPages = 16;
}
}
if( bookAuthor.empty() )
{
CChar *mChar = mSock->CurrcharObj();
if( ValidateObject( mChar ))
{
bookAuthor = mChar->GetName();
}
}
if( bookTitle.empty() )
{
bookTitle = "a book";
}
bInfo.Pages( numPages );
if( isWriteable )
{
bInfo.Flag1( 1 );
bInfo.Flag2( 1 );
}
else
{
bInfo.Flag1( 0 );
bInfo.Flag2( 0 );
}
bInfo.Author( bookAuthor );
bInfo.Title( bookTitle );
bInfo.Finalize();
mSock->Send( &bInfo );
if( bookExists ) // dont send book contents if the file doesnt exist (yet)!
{
mSock->Send( &cpbpToSend );
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CBooks::ReadPreDefBook()
//| Date - 11/5/2005
//o------------------------------------------------------------------------------------------------o
//| Purpose - Sends pager number "p" of a Pre-defined Book to the socket
//o------------------------------------------------------------------------------------------------o
auto CBooks::ReadPreDefBook( CSocket *mSock, CItem *i, UI16 p ) -> void
{
if( mSock )
{
auto temp = "BOOK "s + oldstrutil::number( i->GetTempVar( CITV_MORE ));
CScriptSection *book = FileLookup->FindEntry( temp, misc_def );
if( book )
{
UI16 curPage = p;
for( const auto &sec : book->collection() )
{
auto tag = sec->tag;
if( tag == "PAGE" )
{
--curPage;
if( curPage == 0 ) // we found our page
{
temp = "PAGE "s + sec->data;
auto page = FileLookup->FindEntry( temp, misc_def );
if( page )
{
CPBookPage cpbpSend(( *i ));
cpbpSend.NewPage( p );
for( const auto &sec2 : page->collection() )
{
temp = sec2->data;
cpbpSend.AddLine( temp );
}
cpbpSend.Finalize();
mSock->Send( &cpbpSend );
}
break;
}
}
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIBookPage::Handle()
//| Date - 11/5/2005
//o------------------------------------------------------------------------------------------------o
//| Purpose - Updates the .bok file with changes made on a specific page.
//o------------------------------------------------------------------------------------------------o
bool CPIBookPage::Handle( void )
{
if( tSock != nullptr )
{
CItem *mBook = CalcItemObjFromSer( tSock->GetDWord( 3 ));
if( !ValidateObject( mBook ))
return true;
const UI16 pageNum = tSock->GetWord( 9 );
if( mBook->GetTempVar( CITV_MOREX ) != 666 ) // Just incase, make sure it is a writable book
{
if( mBook->GetTempVar( CITV_MOREX ) != 999 )
{
Books->ReadPreDefBook( tSock, mBook, pageNum );
}
return true;
}
const std::string fileName = cwmWorldState->ServerData()->Directory( CSDDP_BOOKS ) + oldstrutil::number( mBook->GetSerial(), 16 ) + std::string( ".bok" );
UI16 totalLines = tSock->GetWord( 11 );
// Cap amount of lines sent in one go at 8 per page
if( totalLines > 8 )
{
totalLines = 8;
}
// Each page has 8 lines of 34 bytes for each line
char tempLines[8][34];
UI16 bufferCount = 0;
for( UI08 lineNum = 0; lineNum < totalLines; ++lineNum )
{
memset( tempLines[lineNum], 0x00, 34 );
UI08 i = 0;
for( i = 0; i < 34; ++i )
{
tempLines[lineNum][i] = tSock->Buffer()[13+bufferCount];
++bufferCount;
if( tempLines[lineNum][i] == 0x00 )
break;
}
if( tempLines[lineNum][i] != 0x00 )
{
tempLines[lineNum][i] = 0x00;
}
}
if( !FileExists( fileName ))
{
Books->CreateBook( fileName, tSock->CurrcharObj(), mBook );
}
std::fstream file( fileName.c_str(), std::ios::in | std::ios::out | std::ios::binary );
if( file.is_open() )
{
file.seekp(((( static_cast<size_t>( pageNum ) - 1 ) * 272 ) + 32 + 62 + 2 ), std::ios::beg );
if( !file.fail() )
{
for( UI08 j = 0; j < totalLines; ++j )
{
file.write(( const char * )&tempLines[j], 34 );
}
}
else
{
Console.Error( oldstrutil::format( "Failed to seek to book file %s", fileName.c_str() ));
}
file.close();
}
else
{
Console.Error( oldstrutil::format( "Couldn't write to book file %s", fileName.c_str() ));
}
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CBooks::DeleteBook()
//| Date - 11/5/2005
//o------------------------------------------------------------------------------------------------o
//| Purpose - Deletes the .bok file associated with the book item
//o------------------------------------------------------------------------------------------------o
void CBooks::DeleteBook( CItem *id )
{
std::string fileName = cwmWorldState->ServerData()->Directory( CSDDP_BOOKS ) + oldstrutil::number( id->GetSerial(), 16 ) + std::string( ".bok" );
[[maybe_unused]] int removeResult = remove( fileName.c_str() );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CBooks::CreateBook()
//| Date - 11/5/2005
//o------------------------------------------------------------------------------------------------o
//| Purpose - Formats a newly created .bok file, this must be done with any new book file
//o------------------------------------------------------------------------------------------------o
void CBooks::CreateBook( const std::string& fileName, CChar *mChar, CItem *mBook )
{
char wBuffer[2];
char line[34];
char titleBuff[62];
char authBuff[32];
UI16 maxpages = static_cast<UI16>(mBook->GetTempVar( CITV_MOREY ));
if( maxpages < 1 || maxpages > 255 )
{
maxpages = 16;
}
const std::string author = mChar->GetName();
const std::string title = "a book";
memset( line, 0x00, 34 );
memset( titleBuff, 0x00, 62 );
memset( authBuff, 0x00, 32 );
strncopy( titleBuff, 62, title.c_str(), 61 );
strncopy( authBuff, 32, author.c_str(), 31 );
std::ofstream file( fileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc );
file.seekp( 0, std::ios::beg );
file.write( titleBuff, 62 );
file.write( authBuff, 32 );
wBuffer[0] = static_cast<SI08>( maxpages >> 8 );
wBuffer[1] = static_cast<SI08>( maxpages % 256 );
file.write(( const char * )&wBuffer, 2 );
for( UI16 i = 0; i < maxpages; ++i )
{
for( UI08 lineNum = 0; lineNum < 8; ++lineNum )
{
file.write(( const char * )&line, 34 );
}
}
file.close();
}