1 /* -*- C++ -*-
2  *
3  *  ONScripter_text.cpp - Text parser of ONScripter
4  *
5  *  Copyright (c) 2001-2020 Ogapee. All rights reserved.
6  *
7  *  ogapee@aqua.dti2.ne.jp
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #include "ONScripter.h"
25 
26 extern unsigned short convSJIS2UTF16( unsigned short in );
27 
28 #define IS_ROTATION_REQUIRED(x)	\
29     (!IS_TWO_BYTE(*(x)) ||                                              \
30      (*(x) == (char)0x81 && *((x)+1) == (char)0x50) ||                  \
31      (*(x) == (char)0x81 && *((x)+1) == (char)0x51) ||                  \
32      (*(x) == (char)0x81 && *((x)+1) >= 0x5b && *((x)+1) <= 0x5d) ||    \
33      (*(x) == (char)0x81 && *((x)+1) >= 0x60 && *((x)+1) <= 0x64) ||    \
34      (*(x) == (char)0x81 && *((x)+1) >= 0x69 && *((x)+1) <= 0x7a) ||    \
35      (*(x) == (char)0x81 && *((x)+1) == (char)0x80) )
36 
37 #define IS_TRANSLATION_REQUIRED(x)	\
38         ( *(x) == (char)0x81 && *((x)+1) >= 0x41 && *((x)+1) <= 0x44 )
39 
shiftHalfPixelX(SDL_Surface * surface)40 void ONScripter::shiftHalfPixelX(SDL_Surface *surface)
41 {
42     SDL_LockSurface( surface );
43     unsigned char *buf = (unsigned char*)surface->pixels;
44     for (int i=surface->h ; i!=0 ; i--){
45         unsigned char c = buf[0];
46         for (int j=1 ; j<surface->w ; j++){
47             buf[j-1] = (buf[j]+c)>>1;
48             c = buf[j];
49         }
50         buf[surface->w-1] = c>>1;
51         buf += surface->pitch;
52     }
53     SDL_UnlockSurface( surface );
54 }
55 
shiftHalfPixelY(SDL_Surface * surface)56 void ONScripter::shiftHalfPixelY(SDL_Surface *surface)
57 {
58     SDL_LockSurface( surface );
59     for (int j=surface->w-1 ; j>=0 ; j--){
60         unsigned char *buf = (unsigned char*)surface->pixels + j;
61         unsigned char c = buf[0];
62         for (int i=1 ; i<surface->h ; i++){
63             buf += surface->pitch;
64             *(buf-surface->pitch) = (*buf+c)>>1;
65             c = *buf;
66         }
67         *buf = c>>1;
68     }
69     SDL_UnlockSurface( surface );
70 }
71 
drawGlyph(SDL_Surface * dst_surface,FontInfo * info,SDL_Color & color,char * text,int xy[2],AnimationInfo * cache_info,SDL_Rect * clip,SDL_Rect & dst_rect)72 int ONScripter::drawGlyph( SDL_Surface *dst_surface, FontInfo *info, SDL_Color &color, char* text, int xy[2], AnimationInfo *cache_info, SDL_Rect *clip, SDL_Rect &dst_rect )
73 {
74     unsigned short unicode = script_h.enc.getUTF16(text);
75 
76     int minx, maxx, miny, maxy, advanced;
77 #if 0
78     if (TTF_GetFontStyle( (TTF_Font*)info->ttf_font[0] ) !=
79         (info->is_bold?TTF_STYLE_BOLD:TTF_STYLE_NORMAL) )
80         TTF_SetFontStyle( (TTF_Font*)info->ttf_font[0], (info->is_bold?TTF_STYLE_BOLD:TTF_STYLE_NORMAL));
81 #endif
82     TTF_GlyphMetrics( (TTF_Font*)info->ttf_font[0], unicode,
83                       &minx, &maxx, &miny, &maxy, &advanced );
84     //printf("min %d %d %d %d %d %d\n", minx, maxx, miny, maxy, advanced,TTF_FontAscent((TTF_Font*)info->ttf_font[0])  );
85 
86     static SDL_Color fcol={0xff, 0xff, 0xff}, bcol={0, 0, 0};
87     SDL_Surface *tmp_surface = TTF_RenderGlyph_Shaded((TTF_Font*)info->ttf_font[0], unicode, fcol, bcol);
88 
89     SDL_Color scolor = {0, 0, 0};
90     SDL_Surface *tmp_surface_s = tmp_surface;
91     if (info->is_shadow && render_font_outline){
92         unsigned char max_color = color.r;
93         if (max_color < color.g) max_color = color.g;
94         if (max_color < color.b) max_color = color.b;
95         if (max_color < 0x80) scolor.r = 0xff;
96         else                  scolor.r = 0;
97         scolor.g = scolor.b = scolor.r;
98 
99         tmp_surface_s = TTF_RenderGlyph_Shaded((TTF_Font*)info->ttf_font[1], unicode, fcol, bcol);
100         if (tmp_surface && tmp_surface_s){
101             if ((tmp_surface_s->w-tmp_surface->w) & 1) shiftHalfPixelX(tmp_surface_s);
102             if ((tmp_surface_s->h-tmp_surface->h) & 1) shiftHalfPixelY(tmp_surface_s);
103         }
104     }
105 
106     bool rotate_flag = false;
107     if ( info->getTateyokoMode() == FontInfo::TATE_MODE && IS_ROTATION_REQUIRED(text) ) rotate_flag = true;
108 
109     dst_rect.x = xy[0] + minx;
110     dst_rect.y = xy[1] + TTF_FontAscent((TTF_Font*)info->ttf_font[0]) - maxy;
111     if (script_h.enc.getEncoding() == Encoding::CODE_CP932)
112         dst_rect.y -= (TTF_FontHeight((TTF_Font*)info->ttf_font[0]) - info->font_size_xy[1]*screen_ratio1/screen_ratio2)/2;
113 
114     if ( rotate_flag ) dst_rect.x += miny - minx;
115 
116     if ( info->getTateyokoMode() == FontInfo::TATE_MODE && IS_TRANSLATION_REQUIRED(text) ){
117         dst_rect.x += info->font_size_xy[0]/2;
118         dst_rect.y -= info->font_size_xy[0]/2;
119     }
120 
121     if (info->is_shadow && tmp_surface_s){
122         SDL_Rect dst_rect_s = dst_rect;
123         if (render_font_outline){
124             dst_rect_s.x -= (tmp_surface_s->w - tmp_surface->w)/2;
125             dst_rect_s.y -= (tmp_surface_s->h - tmp_surface->h)/2;
126         }
127         else{
128             dst_rect_s.x += shade_distance[0];
129             dst_rect_s.y += shade_distance[1];
130         }
131 
132         if (rotate_flag){
133             dst_rect_s.w = tmp_surface_s->h;
134             dst_rect_s.h = tmp_surface_s->w;
135         }
136         else{
137             dst_rect_s.w = tmp_surface_s->w;
138             dst_rect_s.h = tmp_surface_s->h;
139         }
140 
141         if (cache_info)
142             cache_info->blendText( tmp_surface_s, dst_rect_s.x, dst_rect_s.y, scolor, clip, rotate_flag );
143 
144         if (dst_surface)
145             alphaBlendText( dst_surface, dst_rect_s, tmp_surface_s, scolor, clip, rotate_flag );
146     }
147 
148     if ( tmp_surface ){
149         if (rotate_flag){
150             dst_rect.w = tmp_surface->h;
151             dst_rect.h = tmp_surface->w;
152         }
153         else{
154             dst_rect.w = tmp_surface->w;
155             dst_rect.h = tmp_surface->h;
156         }
157 
158         if (cache_info)
159             cache_info->blendText( tmp_surface, dst_rect.x, dst_rect.y, color, clip, rotate_flag );
160 
161         if (dst_surface)
162             alphaBlendText( dst_surface, dst_rect, tmp_surface, color, clip, rotate_flag );
163     }
164 
165     if (tmp_surface_s && tmp_surface_s != tmp_surface)
166         SDL_FreeSurface(tmp_surface_s);
167     if (tmp_surface)
168         SDL_FreeSurface(tmp_surface);
169 
170     return advanced;
171 }
172 
openFont(FontInfo * fi)173 void ONScripter::openFont(FontInfo *fi)
174 {
175     if (fi->ttf_font[0] == NULL){
176         if (fi->openFont(font_file, screen_ratio1, screen_ratio2) == NULL){
177             fprintf( stderr, "can't open font file: %s\n", font_file );
178             quit();
179             exit(-1);
180         }
181     }
182 #if defined(PSP)
183     else
184         fi->openFont(font_file, screen_ratio1, screen_ratio2);
185 #endif
186 }
187 
drawChar(char * text,FontInfo * info,bool flush_flag,bool lookback_flag,SDL_Surface * surface,AnimationInfo * cache_info,SDL_Rect * clip)188 void ONScripter::drawChar( char* text, FontInfo *info, bool flush_flag, bool lookback_flag, SDL_Surface *surface, AnimationInfo *cache_info, SDL_Rect *clip )
189 {
190     //printf("draw %x-%x[%s] %d, %d\n", text[0], text[1], text, info->xy[0], info->xy[1] );
191     openFont(info);
192 
193     if (info->isEndOfLine()){
194         info->newLine();
195         for (int i=0 ; i<indent_offset ; i++){
196             if (lookback_flag){
197                 if (script_h.enc.getEncoding() == Encoding::CODE_CP932){
198                     current_page->add(0x81);
199                     current_page->add(0x40);
200                 }
201                 else{
202                     current_page->add(0xe3);
203                     current_page->add(0x80);
204                     current_page->add(0x80);
205                 }
206             }
207             info->advanceCharInHankaku(2);
208         }
209     }
210 
211     info->old_xy[0] = info->x(false);
212     info->old_xy[1] = info->y(false);
213 
214     char text2[4] = {};
215     int n = script_h.enc.getBytes(text[0]);
216     for (int i=0; i<n; i++)
217         text2[i] = text[i];
218 
219     for (int i=0 ; i<2 ; i++){
220         int xy[2];
221         xy[0] = info->x() * screen_ratio1 / screen_ratio2;
222         xy[1] = info->y() * screen_ratio1 / screen_ratio2;
223 
224         SDL_Color color = {info->color[0], info->color[1], info->color[2]};
225         SDL_Rect dst_rect;
226         float adv = drawGlyph(surface, info, color, text2, xy, cache_info, clip, dst_rect);
227         if (n == 1) adv -= 0.5; // 0.5 is for adjusting the increse by FT_CEIL
228 
229         if ( surface == accumulation_surface &&
230              !flush_flag &&
231              (!clip || AnimationInfo::doClipping( &dst_rect, clip ) == 0) ){
232             dirty_rect.add( dst_rect );
233         }
234         else if ( flush_flag ){
235             if (info->is_shadow){
236                 if (render_font_outline)
237                     info->addShadeArea(dst_rect, -1, -1, 2, 2);
238                 else
239                     info->addShadeArea(dst_rect, 0, 0, shade_distance[0], shade_distance[1]);
240             }
241             flushDirect( dst_rect, REFRESH_NONE_MODE );
242         }
243 
244         if (n >= 2){
245             if (script_h.enc.getEncoding() == Encoding::CODE_UTF8){
246                 // handle a proportional font
247                 adv += info->pitch_xy[0] - info->font_size_xy[0];
248                 info->advanceCharInHankaku(adv*2.0/info->pitch_xy[0]);
249             }
250             else
251                 info->advanceCharInHankaku(2);
252             break;
253         }
254         if (script_h.enc.getEncoding() == Encoding::CODE_UTF8){
255             // handle a proportional font
256             adv += info->pitch_xy[0] - info->font_size_xy[0];
257             info->advanceCharInHankaku(adv*2.0/info->pitch_xy[0]);
258         }
259         else
260             info->advanceCharInHankaku(1);
261         text2[0] = text[1];
262         if (text2[0] == 0) break;
263     }
264 
265     if (lookback_flag){
266         for (int i=0; i<n; i++)
267             current_page->add(text[i]);
268     }
269 }
270 
drawString(const char * str,uchar3 color,FontInfo * info,bool flush_flag,SDL_Surface * surface,SDL_Rect * rect,AnimationInfo * cache_info,bool pack_hankaku)271 void ONScripter::drawString(const char *str, uchar3 color, FontInfo *info, bool flush_flag, SDL_Surface *surface, SDL_Rect *rect, AnimationInfo *cache_info, bool pack_hankaku)
272 {
273     int i;
274 
275     int start_xy[2];
276     start_xy[0] = info->xy[0];
277     start_xy[1] = info->xy[1];
278 
279     /* ---------------------------------------- */
280     /* Draw selected characters */
281     uchar3 org_color;
282     for ( i=0 ; i<3 ; i++ ) org_color[i] = info->color[i];
283     for ( i=0 ; i<3 ; i++ ) info->color[i] = color[i];
284 
285     bool skip_whitespace_flag = true;
286     if (script_h.enc.getEncoding() == Encoding::CODE_UTF8)
287         skip_whitespace_flag = false;
288     char text[4] = {};
289     while(*str){
290         while (*str == ' ' && skip_whitespace_flag) str++;
291 
292 #ifdef ENABLE_1BYTE_CHAR
293         if ( *str == '`' ){
294             str++;
295             skip_whitespace_flag = false;
296             continue;
297         }
298 #endif
299         if (script_h.enc.getEncoding() == Encoding::CODE_UTF8 &&
300             *str == script_h.enc.getTextMarker()){
301             str++;
302             continue;
303         }
304 
305 #ifndef FORCE_1BYTE_CHAR
306         if (cache_info && !cache_info->is_tight_region){
307             if (*str == '('){
308                 startRuby(str+1, *info);
309                 str++;
310                 continue;
311             }
312             else if (*str == '/' && ruby_struct.stage == RubyStruct::BODY ){
313                 info->addLineOffset(ruby_struct.margin);
314                 str = ruby_struct.ruby_end;
315                 if (*ruby_struct.ruby_end == ')'){
316                     endRuby(false, false, NULL, cache_info);
317                     str++;
318                 }
319                 continue;
320             }
321             else if (*str == ')' && ruby_struct.stage == RubyStruct::BODY ){
322                 ruby_struct.stage = RubyStruct::NONE;
323                 str++;
324                 continue;
325             }
326             else if (*str == '<'){
327                 str++;
328                 int no = 0;
329                 while(*str>='0' && *str<='9')
330                     no=no*10+(*str++)-'0';
331                 in_textbtn_flag = true;
332                 continue;
333             }
334             else if (*str == '>' && in_textbtn_flag){
335                 str++;
336                 in_textbtn_flag = false;
337                 continue;
338             }
339         }
340 #endif
341         int n = script_h.enc.getBytes(*str);
342         if (n >= 2){
343             if (checkLineBreak(str, info)){
344                 info->newLine();
345                 for (int i=0 ; i<indent_offset ; i++)
346                     info->advanceCharInHankaku(2);
347             }
348 
349             for (int i=0; i<n; i++)
350                 text[i] = *str++;
351             drawChar(text, info, false, false, surface, cache_info);
352         }
353         else if (*str == 0x0a || (*str == '\\' && info->is_newline_accepted)){
354             info->newLine();
355             str++;
356         }
357         else if (script_h.enc.getEncoding() == Encoding::CODE_UTF8 &&
358                  *str == '~'){
359             while(1){
360                 str++;
361                 if (*str == 0x0a || *str == 0) break;
362                 if (*str == '~'){
363                     str++;
364                     break;
365                 }
366                 if (*str == 'i'){
367                     openFont(info);
368                     info->toggleStyle(TTF_STYLE_ITALIC);
369                 }
370                 else if (*str == 'b'){
371                     openFont(info);
372                     info->toggleStyle(TTF_STYLE_BOLD);
373                 }
374             }
375             continue;
376         }
377         else if (*str){
378             if (checkLigatureLineBreak(str, info))
379                 info->newLine();
380             text[0] = *str++;
381             if (*str && *str != 0x0a && pack_hankaku &&
382                 script_h.enc.getEncoding() == Encoding::CODE_CP932)
383                 text[1] = *str++;
384             else
385                 text[1] = 0;
386             drawChar( text, info, false, false, surface, cache_info );
387         }
388     }
389     for ( i=0 ; i<3 ; i++ ) info->color[i] = org_color[i];
390 
391     /* ---------------------------------------- */
392     /* Calculate the area of selection */
393     SDL_Rect clipped_rect = info->calcUpdatedArea(start_xy, screen_ratio1, screen_ratio2);
394 
395     SDL_Rect scaled_clipped_rect;
396     scaled_clipped_rect.x = clipped_rect.x * screen_ratio1 / screen_ratio2;
397     scaled_clipped_rect.y = clipped_rect.y * screen_ratio1 / screen_ratio2;
398     scaled_clipped_rect.w = clipped_rect.w * screen_ratio1 / screen_ratio2;
399     scaled_clipped_rect.h = clipped_rect.h * screen_ratio1 / screen_ratio2;
400 
401     if (info->is_shadow){
402         if (render_font_outline)
403             info->addShadeArea(scaled_clipped_rect, -1, -1, 2, 2);
404         else
405             info->addShadeArea(scaled_clipped_rect, 0, 0, shade_distance[0], shade_distance[1]);
406     }
407 
408     if (flush_flag)
409         flush(refresh_shadow_text_mode, &scaled_clipped_rect);
410 
411     if (rect) *rect = clipped_rect;
412 }
413 
restoreTextBuffer(SDL_Surface * surface)414 void ONScripter::restoreTextBuffer(SDL_Surface *surface)
415 {
416     text_info.fill( 0, 0, 0, 0 );
417 
418     char out_text[4] = {};
419     FontInfo f_info = sentence_font;
420     f_info.clear();
421     for (int i=0 ; i<current_page->text_count ; i++){
422         if (current_page->text[i] == 0x0a){
423             f_info.newLine();
424         }
425         else{
426             out_text[0] = current_page->text[i];
427 #ifndef FORCE_1BYTE_CHAR
428             if (out_text[0] == '('){
429                 startRuby(current_page->text + i + 1, f_info);
430                 continue;
431             }
432             else if (out_text[0] == '/' && ruby_struct.stage == RubyStruct::BODY ){
433                 f_info.addLineOffset(ruby_struct.margin);
434                 i = ruby_struct.ruby_end - current_page->text - 1;
435                 if (*ruby_struct.ruby_end == ')'){
436                     endRuby(false, false, surface, &text_info);
437                     i++;
438                 }
439                 continue;
440             }
441             else if (out_text[0] == ')' && ruby_struct.stage == RubyStruct::BODY ){
442                 ruby_struct.stage = RubyStruct::NONE;
443                 continue;
444             }
445             else if (out_text[0] == '<'){
446                 int no = 0;
447                 while(current_page->text[i+1]>='0' && current_page->text[i+1]<='9')
448                     no=no*10+current_page->text[(i++)+1]-'0';
449                 in_textbtn_flag = true;
450                 continue;
451             }
452             else if (out_text[0] == '>' && in_textbtn_flag){
453                 in_textbtn_flag = false;
454                 continue;
455             }
456 #endif
457 
458             int n = script_h.enc.getBytes(out_text[0]);
459             if (n >= 2){
460                 for (int j=1 ; j<n; j++)
461                     out_text[j] = current_page->text[i+j];
462 
463                 if (checkLineBreak(current_page->text+i, &f_info))
464                     f_info.newLine();
465                 i += n-1;
466             }
467             else if (script_h.enc.getEncoding() == Encoding::CODE_UTF8 &&
468                      out_text[0] == '~'){
469                 while(1){
470                     char ch = current_page->text[++i];
471                     if (ch == 0x0a || ch == 0) break;
472                     if (ch == '~'){
473                         i++;
474                         break;
475                     }
476                     if (ch == 'i'){
477                         openFont(&f_info);
478                         f_info.toggleStyle(TTF_STYLE_ITALIC);
479                     }
480                     else if (ch == 'b'){
481                         openFont(&f_info);
482                         f_info.toggleStyle(TTF_STYLE_BOLD);
483                     }
484                 }
485                 continue;
486             }
487             else{
488                 if (checkLigatureLineBreak(current_page->text+i, &f_info))
489                     f_info.newLine();
490 
491                 out_text[1] = 0;
492 
493                 if (i+1 != current_page->text_count &&
494                     current_page->text[i+1] != 0x0a &&
495                     script_h.enc.getEncoding() == Encoding::CODE_CP932){
496                     out_text[1] = current_page->text[i+1];
497                     i++;
498                 }
499             }
500             drawChar(out_text, &f_info, false, false, surface, &text_info);
501         }
502     }
503 }
504 
enterTextDisplayMode(bool text_flag)505 void ONScripter::enterTextDisplayMode(bool text_flag)
506 {
507     if (line_enter_status <= 1 && (!pretextgosub_label || saveon_flag) && internal_saveon_flag && text_flag){
508         storeSaveFile();
509         internal_saveon_flag = false;
510     }
511 
512     if (!(display_mode & DISPLAY_MODE_TEXT)){
513         dirty_rect.clear();
514         dirty_rect.add( sentence_font_info.pos );
515         display_mode = DISPLAY_MODE_TEXT;
516 
517         if (setEffect(&window_effect)) return;
518         while(doEffect(&window_effect, false));
519 
520         text_on_flag = true;
521     }
522 }
523 
leaveTextDisplayMode(bool force_leave_flag)524 void ONScripter::leaveTextDisplayMode(bool force_leave_flag)
525 {
526     if (display_mode & DISPLAY_MODE_TEXT &&
527         (force_leave_flag || erase_text_window_mode != 0)){
528 
529         dirty_rect.add(sentence_font_info.pos);
530         display_mode = DISPLAY_MODE_NORMAL;
531 
532         if (setEffect(&window_effect)) return;
533         while(doEffect(&window_effect, false));
534     }
535 }
536 
doClickEnd()537 bool ONScripter::doClickEnd()
538 {
539     bool ret = false;
540 
541     refresh_shadow_text_mode |= REFRESH_CURSOR_MODE;
542 
543     if ( automode_flag ){
544         event_mode =  WAIT_TEXT_MODE | WAIT_INPUT_MODE | WAIT_VOICE_MODE | WAIT_TIMER_MODE;
545         if ( automode_time < 0 )
546             ret = waitEvent( -automode_time * num_chars_in_sentence );
547         else
548             ret = waitEvent( automode_time );
549     }
550     else if ( autoclick_time > 0 ){
551         event_mode = WAIT_TIMER_MODE;
552         ret = waitEvent( autoclick_time );
553     }
554     else{
555         event_mode = WAIT_TEXT_MODE | WAIT_INPUT_MODE | WAIT_TIMER_MODE;
556         ret = waitEvent(-1);
557     }
558 
559     num_chars_in_sentence = 0;
560 
561     refresh_shadow_text_mode &= ~REFRESH_CURSOR_MODE;
562     stopAnimation( clickstr_state );
563 
564     return ret;
565 }
566 
clickWait(char * out_text)567 bool ONScripter::clickWait( char *out_text )
568 {
569     flush( REFRESH_NONE_MODE );
570     skip_mode &= ~SKIP_TO_EOL;
571 
572     string_buffer_offset += script_h.checkClickstr(script_h.getStringBuffer() + string_buffer_offset);
573 
574     if ( (skip_mode & (SKIP_NORMAL | SKIP_TO_EOP) || ctrl_pressed_status) && !textgosub_label ){
575         clickstr_state = CLICK_NONE;
576         if ( out_text ){
577             drawChar( out_text, &sentence_font, false, true, accumulation_surface, &text_info );
578         }
579         else{ // called on '@'
580             flush(refreshMode());
581         }
582         num_chars_in_sentence = 0;
583 
584         event_mode = IDLE_EVENT_MODE;
585         if ( waitEvent(0) ) return false;
586     }
587     else{
588         if ( out_text ){
589             drawChar( out_text, &sentence_font, true, true, accumulation_surface, &text_info );
590             num_chars_in_sentence++;
591         }
592 
593         while( (!(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR) &&
594                 script_h.getStringBuffer()[ string_buffer_offset ] == ' ') ||
595                script_h.getStringBuffer()[ string_buffer_offset ] == '\t' ) string_buffer_offset ++;
596 
597         if (textgosub_label){
598             saveon_flag = false;
599 
600             textgosub_clickstr_state = CLICK_WAIT;
601             if (script_h.getStringBuffer()[string_buffer_offset] == 0x0)
602                 textgosub_clickstr_state |= CLICK_EOL;
603             gosubReal(textgosub_label, script_h.getWait(), true);
604 
605             event_mode = IDLE_EVENT_MODE;
606             waitEvent(0);
607 
608             return false;
609         }
610 
611         // if this is the end of the line, pretext becomes enabled
612         if (script_h.getStringBuffer()[string_buffer_offset] == 0x0)
613             line_enter_status = 0;
614 
615         clickstr_state = CLICK_WAIT;
616         if (doClickEnd()) return false;
617 
618         clickstr_state = CLICK_NONE;
619 
620         if (pagetag_flag) processEOT();
621         page_enter_status = 0;
622     }
623 
624     return true;
625 }
626 
clickNewPage(char * out_text)627 bool ONScripter::clickNewPage( char *out_text )
628 {
629     if ( out_text ){
630         drawChar( out_text, &sentence_font, true, true, accumulation_surface, &text_info );
631         num_chars_in_sentence++;
632     }
633 
634     flush( REFRESH_NONE_MODE );
635     skip_mode &= ~SKIP_TO_EOL;
636 
637     string_buffer_offset += script_h.checkClickstr(script_h.getStringBuffer() + string_buffer_offset);
638 
639     if ( (skip_mode & SKIP_NORMAL || ctrl_pressed_status) && !textgosub_label  ){
640         num_chars_in_sentence = 0;
641         clickstr_state = CLICK_NEWPAGE;
642 
643         event_mode = IDLE_EVENT_MODE;
644         if (waitEvent(0)) return false;
645     }
646     else{
647         while( (!(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR) &&
648                 script_h.getStringBuffer()[ string_buffer_offset ] == ' ') ||
649                script_h.getStringBuffer()[ string_buffer_offset ] == '\t' ) string_buffer_offset ++;
650 
651         if (textgosub_label){
652             saveon_flag = false;
653 
654             textgosub_clickstr_state = CLICK_NEWPAGE;
655             gosubReal(textgosub_label, script_h.getWait(), true);
656 
657             event_mode = IDLE_EVENT_MODE;
658             waitEvent(0);
659 
660             return false;
661         }
662 
663         // if this is the end of the line, pretext becomes enabled
664         if (script_h.getStringBuffer()[string_buffer_offset] == 0x0)
665             line_enter_status = 0;
666 
667         clickstr_state = CLICK_NEWPAGE;
668         if (doClickEnd()) return false;
669     }
670 
671     newPage();
672     clickstr_state = CLICK_NONE;
673 
674     return true;
675 }
676 
startRuby(const char * buf,FontInfo & info)677 void ONScripter::startRuby(const char *buf, FontInfo &info)
678 {
679     ruby_struct.stage = RubyStruct::BODY;
680     ruby_font = info;
681     ruby_font.ttf_font[0] = NULL;
682     ruby_font.ttf_font[1] = NULL;
683     if ( ruby_struct.font_size_xy[0] != -1 )
684         ruby_font.font_size_xy[0] = ruby_struct.font_size_xy[0];
685     else
686         ruby_font.font_size_xy[0] = info.font_size_xy[0]/2;
687     if ( ruby_struct.font_size_xy[1] != -1 )
688         ruby_font.font_size_xy[1] = ruby_struct.font_size_xy[1];
689     else
690         ruby_font.font_size_xy[1] = info.font_size_xy[1]/2;
691 
692     ruby_struct.body_count = 0;
693     ruby_struct.ruby_count = 0;
694 
695     while(1){
696         if ( *buf == '/' ){
697             ruby_struct.stage = RubyStruct::RUBY;
698             ruby_struct.ruby_start = buf+1;
699             buf++;
700         }
701         else if ( *buf == ')' || *buf == '\0' ){
702             break;
703         }
704         else{
705             int n = script_h.enc.getBytes(*buf);
706             if (ruby_struct.stage == RubyStruct::BODY)
707                 ruby_struct.body_count += (n == 1)?1:2;
708             else if (ruby_struct.stage == RubyStruct::RUBY)
709                 ruby_struct.ruby_count += (n == 1)?1:2;
710             buf += n;
711         }
712     }
713     ruby_struct.ruby_end = buf;
714     ruby_struct.stage = RubyStruct::BODY;
715     ruby_struct.margin = ruby_font.initRuby(info, ruby_struct.body_count/2, ruby_struct.ruby_count/2);
716 }
717 
endRuby(bool flush_flag,bool lookback_flag,SDL_Surface * surface,AnimationInfo * cache_info)718 void ONScripter::endRuby(bool flush_flag, bool lookback_flag, SDL_Surface *surface, AnimationInfo *cache_info)
719 {
720     char out_text[4]= {};
721     if (sentence_font.rubyon_flag){
722         ruby_font.clear();
723         const char *buf = ruby_struct.ruby_start;
724         while(buf < ruby_struct.ruby_end){
725             int n = 2;
726             if (script_h.enc.getEncoding() == Encoding::CODE_UTF8)
727                 n = script_h.enc.getBytes(buf[0]);
728             for (int i=0; i<n; i++)
729                 out_text[i] = buf[i];
730             drawChar(out_text, &ruby_font, flush_flag, lookback_flag, surface, cache_info);
731             buf += n;
732         }
733     }
734     ruby_struct.stage = RubyStruct::NONE;
735 }
736 
textCommand()737 int ONScripter::textCommand()
738 {
739     if (line_enter_status <= 1 && (!pretextgosub_label || saveon_flag) && internal_saveon_flag){
740         storeSaveFile();
741         internal_saveon_flag = false;
742     }
743 
744     char *buf = script_h.getStringBuffer();
745 
746     bool tag_flag = true;
747     unsigned short unicode = script_h.enc.getUTF16("�y", Encoding::CODE_CP932);
748     int n = script_h.enc.getBytes(buf[string_buffer_offset]);
749     if (buf[string_buffer_offset] == '[')
750         string_buffer_offset++;
751     else if (zenkakko_flag &&
752              script_h.enc.getUTF16(buf + string_buffer_offset) == unicode)
753         string_buffer_offset += n;
754     else
755         tag_flag = false;
756 
757     int start_offset = string_buffer_offset;
758     int end_offset = start_offset;
759     while (tag_flag && buf[string_buffer_offset]){
760         unsigned short unicode = script_h.enc.getUTF16("�z", Encoding::CODE_CP932);
761         int n = script_h.enc.getBytes(buf[string_buffer_offset]);
762         if (zenkakko_flag &&
763             script_h.enc.getUTF16(buf + string_buffer_offset) == unicode){
764             end_offset = string_buffer_offset;
765             string_buffer_offset += n;
766             break;
767         }
768         else if (buf[string_buffer_offset] == ']'){
769             end_offset = string_buffer_offset;
770             string_buffer_offset++;
771             break;
772         }
773         else
774             string_buffer_offset += n;
775     }
776 
777     if (pretextgosub_label &&
778         !last_nest_info->pretextgosub_flag &&
779         (!pagetag_flag || page_enter_status == 0) &&
780         line_enter_status == 0){
781 
782         if (current_page->tag) delete[] current_page->tag;
783         if (end_offset > start_offset){
784             int len = end_offset - start_offset;
785             current_page->tag = new char[len+1];
786             memcpy(current_page->tag, buf + start_offset, len);
787             current_page->tag[len] = 0;
788         }
789         else{
790             current_page->tag = NULL;
791         }
792 
793         saveon_flag = false;
794         pretext_buf = script_h.getCurrent();
795         gosubReal(pretextgosub_label, script_h.getCurrent(), false, true);
796         line_enter_status = 1;
797 
798         return RET_CONTINUE;
799     }
800 
801     enterTextDisplayMode();
802 
803 #ifdef USE_LUA
804     if (lua_handler.isCallbackEnabled(LUAHandler::LUA_TEXT))
805     {
806         if (lua_handler.callFunction(true, "text"))
807             errorAndExit( lua_handler.error_str );
808         processEOT();
809     }
810     else
811 #endif
812     while(processText());
813 
814     return RET_CONTINUE;
815 }
816 
checkLineBreak(const char * buf,FontInfo * fi)817 bool ONScripter::checkLineBreak(const char *buf, FontInfo *fi)
818 {
819     if (!is_kinsoku) return false;
820 
821     // check start kinsoku
822     int n = script_h.enc.getBytes(buf[0]);
823     if (isStartKinsoku(buf+n) ||
824         (buf[n]=='_' && isStartKinsoku(buf+n+1))){
825         const char *buf2 = buf + n;
826         if (buf2[0] == '_') buf2++;
827         int i = 2;
828         while (!fi->isEndOfLine(i)){
829             n = script_h.enc.getBytes(buf2[0]);
830             if      (buf2[n] == 0x0a || buf2[n] == 0) break;
831             else if (script_h.enc.getBytes(buf2[n]) == 1) buf2++;
832             else if (isStartKinsoku(buf2 + n)){
833                 i += 2;
834                 buf2 += n;
835             }
836             else break;
837         }
838 
839         if (fi->isEndOfLine(i)) return true;
840     }
841 
842     // check end kinsoku
843     if (isEndKinsoku(buf)){
844         const char *buf2 = buf + n;
845         int i = 2;
846         while (!fi->isEndOfLine(i)){
847             n = script_h.enc.getBytes(buf2[0]);
848             if      (buf2[n] == 0x0a || buf2[n] == 0) break;
849             else if (script_h.enc.getBytes(buf2[n]) == 1) buf2++;
850             else if (isEndKinsoku(buf2 + n)){
851                 i += 2;
852                 buf2 += n;
853             }
854             else break;
855         }
856 
857         if (fi->isEndOfLine(i)) return true;
858     }
859 
860     return false;
861 }
862 
checkLigatureLineBreak(const char * buf,FontInfo * fi)863 bool ONScripter::checkLigatureLineBreak(const char *buf, FontInfo *fi)
864 {
865     if (script_h.current_language != 0) return false;
866 
867     openFont(fi);
868 
869     int w = 0;
870     while (buf[0] != ' ' && buf[0] != 0x0a && buf[0] != 0 &&
871            buf[0] != script_h.enc.getTextMarker()){
872         int n = script_h.enc.getBytes(buf[0]);
873         unsigned short unicode = script_h.enc.getUTF16(buf);
874 
875         int minx, maxx, miny, maxy, advanced;
876         TTF_GlyphMetrics((TTF_Font*)fi->ttf_font[0], unicode,
877                          &minx, &maxx, &miny, &maxy, &advanced);
878 
879         w += advanced + fi->pitch_xy[0] - fi->font_size_xy[0];
880         buf += n;
881     }
882     w -= fi->pitch_xy[0] - fi->font_size_xy[0];
883 
884     return fi->isEndOfLine(w * 2.0 / fi->pitch_xy[0]);
885 }
886 
processEOT()887 void ONScripter::processEOT()
888 {
889     if ( skip_mode & SKIP_TO_EOL ){
890         flush( refreshMode() );
891         skip_mode &= ~SKIP_TO_EOL;
892     }
893 
894     if (!sentence_font.isLineEmpty() && !new_line_skip_flag){
895         // if sentence_font.isLineEmpty() is true, newPage() might be already issued
896         if (!sentence_font.isEndOfLine()) current_page->add( 0x0a );
897         sentence_font.newLine();
898     }
899 
900     if (!new_line_skip_flag && !pagetag_flag && line_enter_status == 2) line_enter_status = 0;
901 }
902 
processText()903 bool ONScripter::processText()
904 {
905     //printf("textCommand %c %d %d %d\n", script_h.getStringBuffer()[ string_buffer_offset ], string_buffer_offset, event_mode, line_enter_status);
906     char out_text[4]= {};
907 
908     //printf("*** textCommand %d (%d,%d)\n", string_buffer_offset, sentence_font.xy[0], sentence_font.xy[1]);
909 
910     while( (!(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR) &&
911             script_h.getStringBuffer()[ string_buffer_offset ] == ' ') ||
912            script_h.getStringBuffer()[ string_buffer_offset ] == '\t' ) string_buffer_offset ++;
913 
914     if (script_h.getStringBuffer()[string_buffer_offset] == 0x00){
915         processEOT();
916         return false;
917     }
918 
919     line_enter_status = 2;
920     if (pagetag_flag) page_enter_status = 1;
921 
922     new_line_skip_flag = false;
923 
924     char ch = script_h.getStringBuffer()[string_buffer_offset];
925     int n = script_h.enc.getBytes(ch);
926     if (n >= 2){
927         /* ---------------------------------------- */
928         /* Kinsoku process */
929         if (checkLineBreak(script_h.getStringBuffer() + string_buffer_offset, &sentence_font)){
930             sentence_font.newLine();
931             for (int i=0 ; i<indent_offset ; i++){
932                 if (script_h.enc.getEncoding() == Encoding::CODE_CP932){
933                     current_page->add(0x81);
934                     current_page->add(0x40);
935                 }
936                 else{
937                     current_page->add(0xe3);
938                     current_page->add(0x80);
939                     current_page->add(0x80);
940                 }
941                 sentence_font.advanceCharInHankaku(2);
942             }
943         }
944 
945         for (int i=0; i<n; i++)
946             out_text[i] = script_h.getStringBuffer()[string_buffer_offset+i];
947 
948         if (script_h.checkClickstr(&script_h.getStringBuffer()[string_buffer_offset]) > 0){
949             if (sentence_font.getRemainingLine() <= clickstr_line)
950                 return clickNewPage( out_text );
951             else
952                 return clickWait( out_text );
953         }
954         else{
955             clickstr_state = CLICK_NONE;
956         }
957 
958         if ( skip_mode || ctrl_pressed_status ){
959             drawChar( out_text, &sentence_font, false, true, accumulation_surface, &text_info );
960         }
961         else{
962             drawChar( out_text, &sentence_font, true, true, accumulation_surface, &text_info );
963 
964             event_mode = WAIT_TIMER_MODE | WAIT_INPUT_MODE;
965             if ( sentence_font.wait_time == -1 )
966                 waitEvent( default_text_speed[text_speed_no] );
967             else
968                 waitEvent( sentence_font.wait_time );
969         }
970 
971         num_chars_in_sentence++;
972         string_buffer_offset += n;
973 
974         return true;
975     }
976     else if ( ch == '@' ){ // wait for click
977         return clickWait( NULL );
978     }
979     else if ( ch == '\\' ){ // new page
980         return clickNewPage( NULL );
981     }
982     else if ( ch == '_' ){ // Ignore an immediate click wait
983         string_buffer_offset++;
984 
985         int matched_len = script_h.checkClickstr(script_h.getStringBuffer() + string_buffer_offset, true);
986         if (matched_len > 0){
987             out_text[0] = script_h.getStringBuffer()[string_buffer_offset];
988             if (out_text[0] != '@' && out_text[0] != '\\'){
989                 for (int i=1; i<matched_len; i++)
990                     out_text[i] = script_h.getStringBuffer()[string_buffer_offset+i];
991                 bool flush_flag = true;
992                 if ( skip_mode || ctrl_pressed_status ) flush_flag = false;
993                 drawChar( out_text, &sentence_font, flush_flag, true, accumulation_surface, &text_info );
994             }
995             string_buffer_offset += matched_len;
996         }
997 
998         return true;
999     }
1000     else if ( ch == '!' && !(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR) ){
1001         string_buffer_offset++;
1002         if ( script_h.getStringBuffer()[ string_buffer_offset ] == 's' ){
1003             string_buffer_offset++;
1004             if ( script_h.getStringBuffer()[ string_buffer_offset ] == 'd' ){
1005                 sentence_font.wait_time = -1;
1006                 string_buffer_offset++;
1007             }
1008             else{
1009                 int t = 0;
1010                 while( script_h.getStringBuffer()[ string_buffer_offset ] >= '0' &&
1011                        script_h.getStringBuffer()[ string_buffer_offset ] <= '9' ){
1012                     t = t*10 + script_h.getStringBuffer()[ string_buffer_offset ] - '0';
1013                     string_buffer_offset++;
1014                 }
1015                 sentence_font.wait_time = t;
1016                 while (script_h.getStringBuffer()[ string_buffer_offset ] == ' ' ||
1017                        script_h.getStringBuffer()[ string_buffer_offset ] == '\t') string_buffer_offset++;
1018             }
1019         }
1020         else if ( script_h.getStringBuffer()[ string_buffer_offset ] == 'w' ||
1021                   script_h.getStringBuffer()[ string_buffer_offset ] == 'd' ){
1022             bool flag = false;
1023             if ( script_h.getStringBuffer()[ string_buffer_offset ] == 'd' ) flag = true;
1024             string_buffer_offset++;
1025             int t = 0;
1026             while( script_h.getStringBuffer()[ string_buffer_offset ] >= '0' &&
1027                    script_h.getStringBuffer()[ string_buffer_offset ] <= '9' ){
1028                 t = t*10 + script_h.getStringBuffer()[ string_buffer_offset ] - '0';
1029                 string_buffer_offset++;
1030             }
1031             while (script_h.getStringBuffer()[ string_buffer_offset ] == ' ' ||
1032                    script_h.getStringBuffer()[ string_buffer_offset ] == '\t') string_buffer_offset++;
1033             if (!skip_mode && !ctrl_pressed_status){
1034                 event_mode = WAIT_TIMER_MODE;
1035                 if (flag) event_mode |= WAIT_INPUT_MODE;
1036                 waitEvent(t);
1037             }
1038         }
1039         return true;
1040     }
1041     else if (ch == '#'){
1042         readColor( &sentence_font.color, script_h.getStringBuffer() + string_buffer_offset );
1043         readColor( &ruby_font.color, script_h.getStringBuffer() + string_buffer_offset );
1044         string_buffer_offset += 7;
1045         return true;
1046     }
1047     else if ( ch == '(' &&
1048               (!english_mode ||
1049                !(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR)) ){
1050         current_page->add('(');
1051         startRuby( script_h.getStringBuffer() + string_buffer_offset + 1, sentence_font );
1052 
1053         string_buffer_offset++;
1054         return true;
1055     }
1056     else if ( ch == '/' && !(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR) ){
1057         if ( ruby_struct.stage == RubyStruct::BODY ){
1058             current_page->add('/');
1059             sentence_font.addLineOffset(ruby_struct.margin);
1060             string_buffer_offset = ruby_struct.ruby_end - script_h.getStringBuffer();
1061             if (*ruby_struct.ruby_end == ')'){
1062                 if ( skip_mode || ctrl_pressed_status )
1063                     endRuby(false, true, accumulation_surface, &text_info);
1064                 else
1065                     endRuby(true, true, accumulation_surface, &text_info);
1066                 current_page->add(')');
1067                 string_buffer_offset++;
1068             }
1069 
1070             return true;
1071         }
1072         else{ // skip new line
1073             new_line_skip_flag = true;
1074             string_buffer_offset++;
1075             if (script_h.getStringBuffer()[string_buffer_offset] != 0x00)
1076                 errorAndExit( "'new line' must follow '/'." );
1077             return true; // skip the following eol
1078         }
1079     }
1080     else if ( ch == ')' && !(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR) &&
1081               ruby_struct.stage == RubyStruct::BODY ){
1082         current_page->add(')');
1083         string_buffer_offset++;
1084         ruby_struct.stage = RubyStruct::NONE;
1085         return true;
1086     }
1087     else if ( ch == '<' &&
1088               (!english_mode ||
1089                !(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR)) ){
1090         current_page->add('<');
1091         string_buffer_offset++;
1092         int no = 0;
1093         while(script_h.getStringBuffer()[string_buffer_offset]>='0' &&
1094               script_h.getStringBuffer()[string_buffer_offset]<='9'){
1095             current_page->add(script_h.getStringBuffer()[string_buffer_offset]);
1096             no=no*10+script_h.getStringBuffer()[string_buffer_offset++]-'0';
1097         }
1098         in_textbtn_flag = true;
1099         return true;
1100     }
1101     else if ( ch == '>' && in_textbtn_flag &&
1102               (!english_mode ||
1103                !(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR)) ){
1104         current_page->add('>');
1105         string_buffer_offset++;
1106         in_textbtn_flag = false;
1107         return true;
1108     }
1109     else if (script_h.enc.getEncoding() == Encoding::CODE_UTF8 &&
1110              ch == script_h.enc.getTextMarker()){
1111         int status = script_h.getEndStatus();
1112         if (status & ScriptHandler::END_1BYTE_CHAR)
1113             script_h.setEndStatus(status & ~ScriptHandler::END_1BYTE_CHAR, true);
1114         else
1115             script_h.setEndStatus(ScriptHandler::END_1BYTE_CHAR);
1116         string_buffer_offset++;
1117 
1118         return true;
1119     }
1120     else if (script_h.enc.getEncoding() == Encoding::CODE_UTF8 &&
1121              ch == '~'){
1122         current_page->add('~');
1123         while(1){
1124             ch = script_h.getStringBuffer()[++string_buffer_offset];
1125             if (ch == 0x0a || ch == 0) break;
1126             current_page->add(ch);
1127             if (ch == '~'){
1128                 string_buffer_offset++;
1129                 break;
1130             }
1131             if (ch == 'i'){
1132                 openFont(&sentence_font);
1133                 sentence_font.toggleStyle(TTF_STYLE_ITALIC);
1134             }
1135             else if (ch == 'b'){
1136                 openFont(&sentence_font);
1137                 sentence_font.toggleStyle(TTF_STYLE_BOLD);
1138             }
1139         }
1140 
1141         return true;
1142     }
1143     else{
1144         out_text[0] = ch;
1145 
1146         int matched_len = script_h.checkClickstr(script_h.getStringBuffer() + string_buffer_offset);
1147 
1148         if (matched_len > 0){
1149             if (matched_len == 2) out_text[1] = script_h.getStringBuffer()[ string_buffer_offset + 1 ];
1150             if (sentence_font.getRemainingLine() <= clickstr_line)
1151                 return clickNewPage( out_text );
1152             else
1153                 return clickWait( out_text );
1154         }
1155         else if (script_h.getStringBuffer()[ string_buffer_offset + 1 ] &&
1156                  script_h.checkClickstr(&script_h.getStringBuffer()[string_buffer_offset+1]) == 1 &&
1157                  script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR){
1158             if ( script_h.getStringBuffer()[ string_buffer_offset + 2 ] &&
1159                  script_h.checkClickstr(&script_h.getStringBuffer()[string_buffer_offset+2]) > 0){
1160                 clickstr_state = CLICK_NONE;
1161             }
1162             else if (script_h.getStringBuffer()[ string_buffer_offset + 1 ] == '@'){
1163                 return clickWait( out_text );
1164             }
1165             else if (script_h.getStringBuffer()[ string_buffer_offset + 1 ] == '\\'){
1166                 return clickNewPage( out_text );
1167             }
1168             else{
1169                 out_text[1] = script_h.getStringBuffer()[ string_buffer_offset + 1 ];
1170                 if (sentence_font.getRemainingLine() <= clickstr_line)
1171                     return clickNewPage( out_text );
1172                 else
1173                     return clickWait( out_text );
1174             }
1175         }
1176         else{
1177             clickstr_state = CLICK_NONE;
1178         }
1179 
1180         bool flush_flag = true;
1181         if ( skip_mode || ctrl_pressed_status )
1182             flush_flag = false;
1183         if (script_h.getStringBuffer()[ string_buffer_offset + 1 ] &&
1184             !(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR) &&
1185             script_h.enc.getEncoding() == Encoding::CODE_CP932){
1186             out_text[1] = script_h.getStringBuffer()[ string_buffer_offset + 1 ];
1187             drawChar(out_text, &sentence_font, flush_flag, true, accumulation_surface, &text_info);
1188             num_chars_in_sentence++;
1189         }
1190         else if (script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR ||
1191                  script_h.enc.getEncoding() == Encoding::CODE_UTF8){
1192             if (checkLigatureLineBreak(script_h.getStringBuffer() + string_buffer_offset, &sentence_font))
1193                 sentence_font.newLine();
1194             drawChar(out_text, &sentence_font, flush_flag, true, accumulation_surface, &text_info);
1195             num_chars_in_sentence++;
1196         }
1197 
1198         if (!skip_mode && !ctrl_pressed_status){
1199             event_mode = WAIT_TIMER_MODE | WAIT_INPUT_MODE;
1200             if ( sentence_font.wait_time == -1 )
1201                 waitEvent( default_text_speed[text_speed_no] );
1202             else
1203                 waitEvent( sentence_font.wait_time );
1204         }
1205 
1206         if (script_h.getStringBuffer()[ string_buffer_offset + 1 ] &&
1207             !(script_h.getEndStatus() & ScriptHandler::END_1BYTE_CHAR) &&
1208             script_h.enc.getEncoding() == Encoding::CODE_CP932)
1209             string_buffer_offset++;
1210         string_buffer_offset++;
1211 
1212         return true;
1213     }
1214 
1215     return false;
1216 }
1217 
1218 
1219