1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2008 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27 
28 #ifndef SRC_SYSTEMS_BASE_RLBABEL_DLL_H_
29 #define SRC_SYSTEMS_BASE_RLBABEL_DLL_H_
30 
31 #include <cstdint>
32 #include <memory>
33 #include <string>
34 #include <vector>
35 
36 #include "machine/reallive_dll.h"
37 #include "machine/reference.h"
38 #include "systems/base/rect.h"
39 
40 class TextWindow;
41 
42 // Possible commands sent to the rlBabel DLL from the code. These will be
43 // passed in as the first integer argument (func) to RlBabelDLL::callDLL().
44 enum dllFunction {
45   dllInitialise = 0,
46   dllTextoutStart = 10,
47   dllTextoutAppend = 11,
48   dllTextoutGetChar = 12,
49   dllTextoutNewScreen = 13,
50   dllClearGlosses = 20,
51   dllNewGloss = 21,
52   dllAddGloss = 22,
53   dllTestGlosses = 23,
54   endSetWindowName = 98,
55   endGetCharWinNam = 99,
56   dllSetNameMod = 100,
57   dllGetNameMod = 101,
58   dllSetWindowName = 102,
59   dllGetTextWindow = 103,
60   dllGetRCommandMod = 104,
61   dllMessageBox = 105,
62   dllSelectAdd = 200
63 };
64 
65 // Return codes from the above functions sent back to the RealLive bytecode.
66 enum getcReturn {
67   getcError = 0,
68   getcEndOfString = 1,
69   getcPrintChar = 2,
70   getcNewLine = 3,
71   getcNewScreen = 4,
72   getcSetIndent = 5,
73   getcClearIndent = 6,
74   getcBeginGloss = 7
75 };
76 
77 // Clickable on screen areas that display a message.
78 class Gloss {
79  public:
80   Gloss(const std::shared_ptr<TextWindow>& window,
81         const std::string& cp932_src,
82         int x1,
83         int y1,
84         int x2,
85         int y2);
86   ~Gloss();
87 
88   // Whether the passed in point intersects with
89   bool Contains(const Point& point);
90 
text()91   const std::string& text() const { return text_; }
92 
93  private:
94   // Text displayed in the gloss.
95   std::string text_;
96 
97   // Clickable areas which trigger this gloss.
98   std::vector<Rect> link_areas_;
99 };  // end of class Gloss
100 
101 // rlvm's implementation of the rlBabel "DLL". Handles calls from
102 // specially compiled RealLive bytecode to implement the following
103 // extra features on top of normal RL bytecode:
104 //
105 // - Text in codepages other than cp932.
106 // - Western text lineation.
107 // - "Glosses," a system of simple hyperlinks.
108 //
109 // How rlBabel works internally:
110 //
111 // Games are disassembled with kprl, their resources are translated,
112 // and are recompiled with rlBabel.kh with a special compiler flag and
113 // the line "#load 'rlBabel'" at the top of the file. rlBabel.kh
114 // redefines several normal functions to be rerouted through a
115 // CallDll() call, along with special casing for textout.
116 //
117 // rlc will compile the disassembled source differently. Instead of
118 // the normal textout method, it will add calls which will put the
119 // text in a buffer (dllTextoutStart, dllTextoutAppend).
120 //
121 // Once everything is placed in the buffer, dllTextoutGetChar will be
122 // called for a list of actions to take (getcNewLine, getcNewScreen,
123 // getcSetIndent, getcPrintChar, etc.) Each character will be pulled
124 // out of the buffer (getcPrintChar) and displayed, moving the text
125 // insertion point to a location provided by the DLL.
126 class RlBabelDLL : public RealLiveDLL {
127  public:
128   explicit RlBabelDLL(RLMachine& machine);
129   virtual ~RlBabelDLL();
130 
131   // Overridden from RealLiveDLL:
132 
133   // Main entrypoint to the "DLL". It's a giant switch function that handles
134   // all the commands that Haeleth added with rlBabel.
135   virtual int CallDLL(RLMachine& machine,
136                       int func,
137                       int arg1,
138                       int arg2,
139                       int arg3,
140                       int arg4) override;
141 
142   virtual const std::string& GetDLLName() const override;
143 
144  private:
145   // Initializes the DLL.
146   int Initialize(int dllno, int windname);
147 
148   // Takes an input string and copies it to our internal buffer.
149   int TextoutAdd(const std::string& str);
150 
151   // Adds characters to the internal buffer, italicizing text as it comes in.
152   void AppendChar(const char*& ch);
153 
154   // Clears our internal text buffer.
155   void TextoutClear();
156 
157   // Checks if there's room on this page, and either line breaks (returns
158   // getcNewLine) or page breaks (returns getcNewScreen).
159   int TextoutLineBreak(StringReferenceIterator buf);
160 
161   // Retrieves an action specified in getcReturn, which directs the side of
162   // rlBabel implemented in RealLive code. Uses buffer and xmod as output
163   // variables for the command given.
164   int TextoutGetChar(StringReferenceIterator buffer, IntReferenceIterator xmod);
165 
166   // (rlBabel function not entirely understood...)
167   int StartNewScreen(const std::string& cnam);
168 
169   // Sets the window name internally. This does not display the name in the
170   // case of NAME_MOD being 0 (name displayed inline), but will display it in
171   // case of NAME_MOD being 1 (name being displayed in a different window
172   // where it won't mess with our indentation rules.)
173   int SetCurrentWindowName(StringReferenceIterator buffer);
174 
175   // Clears all on screen glosses.
176   int ClearGlosses();
177 
178   // Mark where this gloss begins.
179   int NewGloss();
180 
181   // Create a gloss of the text since newGloss() with the glosstext of
182   // |cp932_gloss_text|.
183   int AddGloss(const std::string& cp932_gloss_text);
184 
185   // Tests if (x, y) is inside any defined glosses. If so, return true and put
186   // the glosstext in |text|.
187   int TestGlosses(int x, int y, StringReferenceIterator text, int globalwaku);
188 
189   // Helper functions:
190 
191   int GetCharWidth(uint16_t full_char, bool as_xmod);
192 
193   bool LineBreakRequired();
194 
195   uint16_t ConsumeNextCharacter(std::string::size_type& index);
196 
197   inline char& cur_pos(int offset = 0) {
198     return cp932_text_buffer[text_index + offset];
199   }
200   inline const char& cur_pos(int offset = 0) const {
201     return cp932_text_buffer[text_index + offset];
202   }
203   inline char& end_token(int offset = 0) {
204     return cp932_text_buffer[end_token_index + offset];
205   }
206   inline const char& end_token(int offset = 0) const {
207     return cp932_text_buffer[end_token_index + offset];
208   }
209 
210   // Transform one of rlBabel's integer addresses into an iterator to the
211   // corresponding piece of integer memory.
212   IntReferenceIterator GetIvar(int addr);
213 
214   // Transform one of rlBabel's integer addresses into an iterator to the
215   // corresponding piece of integer memory.
216   StringReferenceIterator GetSvar(int addr);
217 
218   std::shared_ptr<TextWindow> GetWindow(int id);
219 
220   // Whether text being added is italicized.
221   bool add_is_italic;
222 
223   // Internal text buffer to which text is added by dllTextoutStart and
224   // dllTextoutAppend. Neither |text_index| or |cp932_text_buffer| are
225   // iterators or pointers into this strings character backing since I'm
226   // worried about invalidation.
227   std::string cp932_text_buffer;
228 
229   // Current position in |cp932_text_buffer|.
230   std::string::size_type text_index;
231 
232   // End of the current token being processed in |cp932_text_buffer|.
233   std::string::size_type end_token_index;
234 
235   // Clickable on screen areas that display a message.
236   std::vector<Gloss> glosses_;
237 
238   // Marker set at the start of a gloss.
239   int gloss_start_x_, gloss_start_y_;
240 
241   // Reference to our enclosing system.
242   RLMachine& machine_;
243 };  // end of class RlBabelDll
244 
245 #endif  // SRC_SYSTEMS_BASE_RLBABEL_DLL_H_
246