5
5
#include < stdlib.h>
6
6
#include < string.h>
7
7
8
+ #include < array>
9
+ #include < unordered_map>
10
+
8
11
#include " debug.h"
9
12
#include " memory.h"
10
13
#include " platform_compat.h"
@@ -17,11 +20,33 @@ namespace fallout {
17
20
18
21
#define BADWORD_LENGTH_MAX 80
19
22
23
+ static constexpr int kFirstStandardMessageListId = 0 ;
24
+ static constexpr int kLastStandardMessageListId = kFirstStandardMessageListId + STANDARD_MESSAGE_LIST_COUNT - 1 ;
25
+
26
+ static constexpr int kFirstProtoMessageListId = 0x1000 ;
27
+ static constexpr int kLastProtoMessageListId = kFirstProtoMessageListId + PROTO_MESSAGE_LIST_COUNT - 1 ;
28
+
29
+ static constexpr int kFirstPersistentMessageListId = 0x2000 ;
30
+ static constexpr int kLastPersistentMessageListId = 0x2FFF ;
31
+
32
+ static constexpr int kFirstTemporaryMessageListId = 0x3000 ;
33
+ static constexpr int kLastTemporaryMessageListId = 0x3FFF ;
34
+
35
+ struct MessageListRepositoryState {
36
+ std::array<MessageList*, STANDARD_MESSAGE_LIST_COUNT> standardMessageLists;
37
+ std::array<MessageList*, PROTO_MESSAGE_LIST_COUNT> protoMessageLists;
38
+ std::unordered_map<int , MessageList*> persistentMessageLists;
39
+ std::unordered_map<int , MessageList*> temporaryMessageLists;
40
+ int nextTemporaryMessageListId = kFirstTemporaryMessageListId ;
41
+ };
42
+
20
43
static bool _message_find (MessageList* msg, int num, int * out_index);
21
44
static bool _message_add (MessageList* msg, MessageListItem* new_entry);
22
45
static bool _message_parse_number (int * out_num, const char * str);
23
46
static int _message_load_field (File* file, char * str);
24
47
48
+ static MessageList* messageListRepositoryLoad (const char * path);
49
+
25
50
// 0x50B79C
26
51
static char _Error_1[] = " Error" ;
27
52
@@ -47,6 +72,8 @@ static char* _message_error_str = _Error_1;
47
72
// 0x63207C
48
73
static char _bad_copy[MESSAGE_LIST_ITEM_FIELD_MAX_SIZE];
49
74
75
+ static MessageListRepositoryState* _messageListRepositoryState;
76
+
50
77
// 0x484770
51
78
int badwordsInit ()
52
79
{
@@ -604,4 +631,187 @@ void messageListFilterGenderWords(MessageList* messageList, int gender)
604
631
}
605
632
}
606
633
634
+ bool messageListRepositoryInit ()
635
+ {
636
+ _messageListRepositoryState = new (std::nothrow) MessageListRepositoryState ();
637
+ if (_messageListRepositoryState == nullptr ) {
638
+ return false ;
639
+ }
640
+
641
+ char * fileList;
642
+ configGetString (&gSfallConfig , SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY, &fileList);
643
+ if (fileList != nullptr && *fileList == ' \0 ' ) {
644
+ fileList = nullptr ;
645
+ }
646
+
647
+ char path[COMPAT_MAX_PATH];
648
+ int nextMessageListId = 0 ;
649
+ while (fileList != nullptr ) {
650
+ char * pch = strchr (fileList, ' ,' );
651
+ if (pch != nullptr ) {
652
+ *pch = ' \0 ' ;
653
+ }
654
+
655
+ char * sep = strchr (fileList, ' :' );
656
+ if (sep != nullptr ) {
657
+ *sep = ' \0 ' ;
658
+ nextMessageListId = atoi (sep + 1 );
659
+ }
660
+
661
+ sprintf (path, " %s\\ %s.msg" , " game" , fileList);
662
+
663
+ if (sep != nullptr ) {
664
+ *sep = ' :' ;
665
+ }
666
+
667
+ MessageList* messageList = messageListRepositoryLoad (path);
668
+ if (messageList != nullptr ) {
669
+ _messageListRepositoryState->persistentMessageLists [kFirstPersistentMessageListId + nextMessageListId] = messageList;
670
+ }
671
+
672
+ if (pch != nullptr ) {
673
+ *pch = ' ,' ;
674
+ fileList = pch + 1 ;
675
+ } else {
676
+ fileList = nullptr ;
677
+ }
678
+
679
+ // Sfall's implementation is a little bit odd. |nextMessageListId| can
680
+ // be set via "key:value" pair in the config, so if the first pair is
681
+ // "msg:12287", then this check will think it's the end of the loop.
682
+ // In order to maintain compatibility we'll use the same approach,
683
+ // however it looks like the whole idea of auto-numbering extra message
684
+ // lists is a bad one. To use these extra message lists we need to
685
+ // specify their ids from user-space scripts. Without explicitly
686
+ // specifying message list ids as "key:value" pairs a mere change of
687
+ // order in the config will break such scripts in an unexpected way.
688
+ nextMessageListId++;
689
+ if (nextMessageListId == kLastPersistentMessageListId - kFirstPersistentMessageListId + 1 ) {
690
+ break ;
691
+ }
692
+ }
693
+
694
+ return true ;
695
+ }
696
+
697
+ void messageListRepositoryReset ()
698
+ {
699
+ for (auto & pair : _messageListRepositoryState->temporaryMessageLists ) {
700
+ messageListFree (pair.second );
701
+ delete pair.second ;
702
+ }
703
+ _messageListRepositoryState->temporaryMessageLists .clear ();
704
+ _messageListRepositoryState->nextTemporaryMessageListId = kFirstTemporaryMessageListId ;
705
+ }
706
+
707
+ void messageListRepositoryExit ()
708
+ {
709
+ if (_messageListRepositoryState != nullptr ) {
710
+ for (auto & pair : _messageListRepositoryState->temporaryMessageLists ) {
711
+ messageListFree (pair.second );
712
+ delete pair.second ;
713
+ }
714
+
715
+ for (auto & pair : _messageListRepositoryState->persistentMessageLists ) {
716
+ messageListFree (pair.second );
717
+ delete pair.second ;
718
+ }
719
+
720
+ delete _messageListRepositoryState;
721
+ _messageListRepositoryState = nullptr ;
722
+ }
723
+ }
724
+
725
+ void messageListRepositorySetStandardMessageList (int standardMessageList, MessageList* messageList)
726
+ {
727
+ _messageListRepositoryState->standardMessageLists [standardMessageList] = messageList;
728
+ }
729
+
730
+ void messageListRepositorySetProtoMessageList (int protoMessageList, MessageList* messageList)
731
+ {
732
+ _messageListRepositoryState->protoMessageLists [protoMessageList] = messageList;
733
+ }
734
+
735
+ int messageListRepositoryAddExtra (int messageListId, const char * path)
736
+ {
737
+ if (messageListId != 0 ) {
738
+ // CE: Probably there is a bug in Sfall, when |messageListId| is
739
+ // non-zero, it is enforced to be within persistent id range. That is
740
+ // the scripting engine is allowed to add persistent message lists.
741
+ // Everything added/changed by scripting engine should be temporary by
742
+ // design.
743
+ if (messageListId < kFirstPersistentMessageListId || messageListId > kLastPersistentMessageListId ) {
744
+ return -1 ;
745
+ }
746
+
747
+ // CE: Sfall stores both persistent and temporary message lists in
748
+ // one map, however since we've passed check above, we should only
749
+ // check in persistent message lists.
750
+ if (_messageListRepositoryState->persistentMessageLists .find (messageListId) != _messageListRepositoryState->persistentMessageLists .end ()) {
751
+ return 0 ;
752
+ }
753
+ } else {
754
+ if (_messageListRepositoryState->nextTemporaryMessageListId > kLastTemporaryMessageListId ) {
755
+ return -3 ;
756
+ }
757
+ }
758
+
759
+ MessageList* messageList = messageListRepositoryLoad (path);
760
+ if (messageList == nullptr ) {
761
+ return -2 ;
762
+ }
763
+
764
+ if (messageListId == 0 ) {
765
+ messageListId == _messageListRepositoryState->nextTemporaryMessageListId ++;
766
+ }
767
+
768
+ _messageListRepositoryState->temporaryMessageLists [messageListId] = messageList;
769
+
770
+ return messageListId;
771
+ }
772
+
773
+ char * messageListRepositoryGetMsg (int messageListId, int messageId)
774
+ {
775
+ MessageList* messageList = nullptr ;
776
+
777
+ if (messageListId >= kFirstStandardMessageListId && messageListId <= kLastStandardMessageListId ) {
778
+ messageList = _messageListRepositoryState->standardMessageLists [messageListId - kFirstStandardMessageListId ];
779
+ } else if (messageListId >= kFirstProtoMessageListId && messageListId <= kLastProtoMessageListId ) {
780
+ messageList = _messageListRepositoryState->protoMessageLists [messageListId - kFirstProtoMessageListId ];
781
+ } else if (messageListId >= kFirstPersistentMessageListId && messageListId <= kLastPersistentMessageListId ) {
782
+ auto it = _messageListRepositoryState->persistentMessageLists .find (messageListId);
783
+ if (it != _messageListRepositoryState->persistentMessageLists .end ()) {
784
+ messageList = it->second ;
785
+ }
786
+ } else if (messageListId >= kFirstTemporaryMessageListId && messageListId <= kLastTemporaryMessageListId ) {
787
+ auto it = _messageListRepositoryState->temporaryMessageLists .find (messageListId);
788
+ if (it != _messageListRepositoryState->temporaryMessageLists .end ()) {
789
+ messageList = it->second ;
790
+ }
791
+ }
792
+
793
+ MessageListItem messageListItem;
794
+ return getmsg (messageList, &messageListItem, messageId);
795
+ }
796
+
797
+ static MessageList* messageListRepositoryLoad (const char * path)
798
+ {
799
+ MessageList* messageList = new (std::nothrow) MessageList ();
800
+ if (messageList == nullptr ) {
801
+ return nullptr ;
802
+ }
803
+
804
+ if (!messageListInit (messageList)) {
805
+ delete messageList;
806
+ return nullptr ;
807
+ }
808
+
809
+ if (!messageListLoad (messageList, path)) {
810
+ delete messageList;
811
+ return nullptr ;
812
+ }
813
+
814
+ return messageList;
815
+ }
816
+
607
817
} // namespace fallout
0 commit comments