gosu 0.14.0 → 0.14.3.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,69 +1,69 @@
1
- #include <Gosu/Platform.hpp>
2
- #if defined(GOSU_IS_WIN)
3
-
4
- #include "WinUtility.hpp"
5
- #include <Gosu/Directories.hpp>
6
- #include <Gosu/Utility.hpp>
7
- #include <cwchar>
8
- #include <stdexcept>
1
+ #include <Gosu/Platform.hpp>
2
+ #if defined(GOSU_IS_WIN)
3
+
4
+ #include "WinUtility.hpp"
5
+ #include <Gosu/Directories.hpp>
6
+ #include <Gosu/Utility.hpp>
7
+ #include <cwchar>
8
+ #include <stdexcept>
9
9
  #include <shlobj.h>
10
- using namespace std;
11
-
12
- static string special_folder_path(int csidl)
13
- {
14
- WCHAR buf[MAX_PATH + 2];
15
- if (FAILED(SHGetFolderPathW(nullptr, csidl | CSIDL_FLAG_CREATE, nullptr, 0, buf))) {
16
- throw runtime_error("Error getting special folder path");
17
- }
18
- size_t len = wcslen(buf);
19
- if (buf[len - 1] != L'\\') {
20
- buf[len] = L'\\';
21
- buf[len + 1] = 0;
22
- }
23
- return Gosu::utf16_to_utf8(buf);
24
- }
25
-
26
- static string exe_filename()
27
- {
28
- static string result;
29
- if (result.empty()) {
30
- WCHAR buffer[MAX_PATH * 2];
31
- Gosu::winapi_check(GetModuleFileNameW(nullptr, buffer, MAX_PATH * 2),
32
- "getting the module filename");
33
- result = Gosu::utf16_to_utf8(buffer);
34
- }
35
- return result;
36
- }
37
-
38
- void Gosu::use_resource_directory()
39
- {
40
- SetCurrentDirectory(utf8_to_utf16(resource_prefix()).c_str());
41
- }
42
-
43
- string Gosu::resource_prefix()
44
- {
45
- static string result;
46
- if (result.empty()) {
47
- result = exe_filename();
48
- auto last_delim = result.find_last_of("\\/");
49
- result.resize(last_delim == string::npos ? 0 : last_delim + 1);
50
- }
51
- return result;
52
- }
53
-
54
- string Gosu::shared_resource_prefix()
55
- {
56
- return resource_prefix();
57
- }
58
-
59
- string Gosu::user_settings_prefix()
60
- {
61
- return special_folder_path(CSIDL_APPDATA);
62
- }
63
-
64
- string Gosu::user_documents_prefix()
65
- {
66
- return special_folder_path(CSIDL_PERSONAL);
67
- }
68
-
69
- #endif
10
+ using namespace std;
11
+
12
+ static string special_folder_path(int csidl)
13
+ {
14
+ WCHAR buf[MAX_PATH + 2];
15
+ if (FAILED(SHGetFolderPathW(nullptr, csidl | CSIDL_FLAG_CREATE, nullptr, 0, buf))) {
16
+ throw runtime_error("Error getting special folder path");
17
+ }
18
+ size_t len = wcslen(buf);
19
+ if (buf[len - 1] != L'\\') {
20
+ buf[len] = L'\\';
21
+ buf[len + 1] = 0;
22
+ }
23
+ return Gosu::utf16_to_utf8(buf);
24
+ }
25
+
26
+ static string exe_filename()
27
+ {
28
+ static string result;
29
+ if (result.empty()) {
30
+ WCHAR buffer[MAX_PATH * 2];
31
+ Gosu::winapi_check(GetModuleFileNameW(nullptr, buffer, MAX_PATH * 2),
32
+ "getting the module filename");
33
+ result = Gosu::utf16_to_utf8(buffer);
34
+ }
35
+ return result;
36
+ }
37
+
38
+ void Gosu::use_resource_directory()
39
+ {
40
+ SetCurrentDirectory(utf8_to_utf16(resource_prefix()).c_str());
41
+ }
42
+
43
+ string Gosu::resource_prefix()
44
+ {
45
+ static string result;
46
+ if (result.empty()) {
47
+ result = exe_filename();
48
+ auto last_delim = result.find_last_of("\\/");
49
+ result.resize(last_delim == string::npos ? 0 : last_delim + 1);
50
+ }
51
+ return result;
52
+ }
53
+
54
+ string Gosu::shared_resource_prefix()
55
+ {
56
+ return resource_prefix();
57
+ }
58
+
59
+ string Gosu::user_settings_prefix()
60
+ {
61
+ return special_folder_path(CSIDL_APPDATA);
62
+ }
63
+
64
+ string Gosu::user_documents_prefix()
65
+ {
66
+ return special_folder_path(CSIDL_PERSONAL);
67
+ }
68
+
69
+ #endif
data/src/Graphics.cpp CHANGED
@@ -265,7 +265,8 @@ void Gosu::Graphics::clip_to(double x, double y, double width, double height,
265
265
  current_queue().end_clipping();
266
266
  }
267
267
 
268
- Gosu::Image Gosu::Graphics::render(int width, int height, const function<void ()>& f)
268
+ Gosu::Image Gosu::Graphics::render(int width, int height, const function<void ()>& f,
269
+ unsigned image_flags)
269
270
  {
270
271
  ensure_current_context();
271
272
 
@@ -285,7 +286,7 @@ Gosu::Image Gosu::Graphics::render(int width, int height, const function<void ()
285
286
  #endif
286
287
 
287
288
  // This is the actual render-to-texture step.
288
- Image result = OffScreenTarget(width, height).render([&] {
289
+ Image result = OffScreenTarget(width, height, image_flags).render([&] {
289
290
  glClearColor(0, 0, 0, 0);
290
291
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
291
292
  queues.emplace_back(QM_RENDER_TO_TEXTURE);
data/src/InputUIKit.cpp CHANGED
@@ -171,7 +171,8 @@ void Gosu::Input::set_text_input(TextInput* text_input)
171
171
  if (text_input) {
172
172
  pimpl->text_input = text_input;
173
173
  [pimpl->view becomeFirstResponder];
174
- } else {
174
+ }
175
+ else {
175
176
  [pimpl->view resignFirstResponder];
176
177
  pimpl->text_input = nullptr;
177
178
  }
data/src/MarkupParser.cpp CHANGED
@@ -139,7 +139,7 @@ bool Gosu::MarkupParser::parse_escape_entity()
139
139
 
140
140
  void Gosu::MarkupParser::add_current_substring()
141
141
  {
142
- if (! substring.empty()) {
142
+ if (!substring.empty()) {
143
143
  add_composed_substring(utf8_to_composed_utc4(substring));
144
144
  substring.clear();
145
145
  }
@@ -152,7 +152,7 @@ void Gosu::MarkupParser::add_composed_substring(u32string&& substring)
152
152
  fstr.flags = flags();
153
153
  fstr.color = c.back();
154
154
 
155
- if (! substrings.empty() && substrings.back().can_be_merged_with(fstr)) {
155
+ if (!substrings.empty() && substrings.back().can_be_merged_with(fstr)) {
156
156
  substrings.back().text.append(move(fstr.text));
157
157
  }
158
158
  else {
@@ -162,7 +162,7 @@ void Gosu::MarkupParser::add_composed_substring(u32string&& substring)
162
162
 
163
163
  void Gosu::MarkupParser::flush_to_consumer()
164
164
  {
165
- if (! substrings.empty()) {
165
+ if (!substrings.empty()) {
166
166
  consumer(move(substrings));
167
167
  substrings.clear();
168
168
  }
@@ -221,7 +221,8 @@ void Gosu::MarkupParser::parse(const std::string& markup_string)
221
221
  add_current_substring();
222
222
  flush_to_consumer();
223
223
  word_state = ADDING_WHITESPACE;
224
- } else if (!whitespace_except_newline && word_state == ADDING_WHITESPACE) {
224
+ }
225
+ else if (!whitespace_except_newline && word_state == ADDING_WHITESPACE) {
225
226
  // We are in word-parsing mode, and this is was the start of a word.
226
227
  add_current_substring();
227
228
  flush_to_consumer();
@@ -28,7 +28,7 @@ using namespace std;
28
28
  GL_DEPTH_COMPONENT
29
29
  #endif
30
30
 
31
- Gosu::OffScreenTarget::OffScreenTarget(int width, int height)
31
+ Gosu::OffScreenTarget::OffScreenTarget(int width, int height, unsigned image_flags)
32
32
  {
33
33
  #ifndef GOSU_IS_IPHONE
34
34
  if (!SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object")) {
@@ -37,7 +37,7 @@ Gosu::OffScreenTarget::OffScreenTarget(int width, int height)
37
37
  #endif
38
38
 
39
39
  // Create a new texture that will be our rendering target.
40
- texture = make_shared<Texture>(width, height, false);
40
+ texture = make_shared<Texture>(width, height, image_flags & IF_RETRO);
41
41
  // Mark the full texture as blocked for our TexChunk.
42
42
  texture->block(0, 0, width, height);
43
43
 
@@ -16,7 +16,7 @@ namespace Gosu
16
16
  OffScreenTarget& operator=(OffScreenTarget&& other) = delete;
17
17
 
18
18
  public:
19
- OffScreenTarget(int width, int height);
19
+ OffScreenTarget(int width, int height, unsigned image_flags);
20
20
  ~OffScreenTarget();
21
21
  Gosu::Image render(const std::function<void ()>& f);
22
22
  };
data/src/Resolution.cpp CHANGED
@@ -34,7 +34,7 @@ unsigned Gosu::screen_height(Window* window)
34
34
  static NSSize max_window_size(Gosu::Window* window)
35
35
  {
36
36
  // Keep in sync with SDL_cocoawindow.m.
37
- auto style = NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable;
37
+ auto style = NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask;
38
38
 
39
39
  auto index = window ? SDL_GetWindowDisplayIndex(Gosu::shared_window()) : 0;
40
40
  auto screen_frame = NSScreen.screens[index].visibleFrame;
@@ -63,7 +63,8 @@ static SIZE max_window_size(Gosu::Window* window)
63
63
  if (window == nullptr) {
64
64
  // Easy case: Return the work area of the primary monitor.
65
65
  SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0);
66
- } else {
66
+ }
67
+ else {
67
68
  // Return the work area of the monitor the window is on.
68
69
  SDL_SysWMinfo wm_info;
69
70
  SDL_VERSION(&wm_info.version);
data/src/RubyGosu.cxx CHANGED
@@ -2408,8 +2408,34 @@ namespace Gosu
2408
2408
  return new Gosu::Image(Gosu::Graphics::record(width, height, [] { rb_yield(Qnil); }));
2409
2409
  }
2410
2410
 
2411
- Gosu::Image* render(int width, int height) {
2412
- return new Gosu::Image(Gosu::Graphics::render(width, height, [] { rb_yield(Qnil); }));
2411
+ Gosu::Image* render(int width, int height, VALUE options = 0) {
2412
+ unsigned image_flags = 0;
2413
+
2414
+ if (options) {
2415
+ Check_Type(options, T_HASH);
2416
+
2417
+ VALUE keys = rb_funcall(options, rb_intern("keys"), 0, NULL);
2418
+ int keys_size = NUM2INT(rb_funcall(keys, rb_intern("size"), 0, NULL));
2419
+
2420
+ for (int i = 0; i < keys_size; ++i) {
2421
+ VALUE key = rb_ary_entry(keys, i);
2422
+ const char* key_string = Gosu::cstr_from_symbol(key);
2423
+
2424
+ VALUE value = rb_hash_aref(options, key);
2425
+ if (!strcmp(key_string, "retro")) {
2426
+ if (RTEST(value)) image_flags |= Gosu::IF_RETRO;
2427
+ }
2428
+ else {
2429
+ static bool issued_warning = false;
2430
+ if (!issued_warning) {
2431
+ issued_warning = true;
2432
+ rb_warn("Unknown keyword argument: :%s", key_string);
2433
+ }
2434
+ }
2435
+ }
2436
+ }
2437
+
2438
+ return new Gosu::Image(Gosu::Graphics::render(width, height, [] { rb_yield(Qnil); }, image_flags));
2413
2439
  }
2414
2440
 
2415
2441
  // This method cannot be called "transform" because then it would be an ambiguous overload of
@@ -10890,6 +10916,7 @@ SWIGINTERN VALUE
10890
10916
  _wrap_render(int argc, VALUE *argv, VALUE self) {
10891
10917
  int arg1 ;
10892
10918
  int arg2 ;
10919
+ VALUE arg3 = (VALUE) 0 ;
10893
10920
  int val1 ;
10894
10921
  int ecode1 = 0 ;
10895
10922
  int val2 ;
@@ -10897,7 +10924,7 @@ _wrap_render(int argc, VALUE *argv, VALUE self) {
10897
10924
  Gosu::Image *result = 0 ;
10898
10925
  VALUE vresult = Qnil;
10899
10926
 
10900
- if ((argc < 2) || (argc > 2)) {
10927
+ if ((argc < 2) || (argc > 3)) {
10901
10928
  rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)",argc); SWIG_fail;
10902
10929
  }
10903
10930
  ecode1 = SWIG_AsVal_int(argv[0], &val1);
@@ -10910,15 +10937,18 @@ _wrap_render(int argc, VALUE *argv, VALUE self) {
10910
10937
  SWIG_exception_fail(SWIG_ArgError(ecode2), Ruby_Format_TypeError( "", "int","Gosu::render", 2, argv[1] ));
10911
10938
  }
10912
10939
  arg2 = static_cast< int >(val2);
10940
+ if (argc > 2) {
10941
+ arg3 = argv[2];
10942
+ }
10913
10943
  {
10914
10944
  try {
10915
- result = (Gosu::Image *)Gosu::render(arg1,arg2);
10945
+ result = (Gosu::Image *)Gosu::render(arg1,arg2,arg3);
10916
10946
  }
10917
10947
  catch (const std::exception& e) {
10918
10948
  SWIG_exception(SWIG_RuntimeError, e.what());
10919
10949
  }
10920
10950
  }
10921
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Gosu__Image, 0 | 0 );
10951
+ vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Gosu__Image, SWIG_POINTER_OWN | 0 );
10922
10952
  return vresult;
10923
10953
  fail:
10924
10954
  return Qnil;
@@ -11671,7 +11701,7 @@ SWIGEXPORT void Init_gosu(void) {
11671
11701
  rb_define_const(mGosu, "LICENSES", SWIG_From_std_string(static_cast< std::string >(Gosu::LICENSES)));
11672
11702
  rb_define_const(mGosu, "MAJOR_VERSION", SWIG_From_int(static_cast< int >(0)));
11673
11703
  rb_define_const(mGosu, "MINOR_VERSION", SWIG_From_int(static_cast< int >(14)));
11674
- rb_define_const(mGosu, "POINT_VERSION", SWIG_From_int(static_cast< int >(0)));
11704
+ rb_define_const(mGosu, "POINT_VERSION", SWIG_From_int(static_cast< int >(3)));
11675
11705
  rb_define_module_function(mGosu, "milliseconds", VALUEFUNC(_wrap_milliseconds), -1);
11676
11706
  rb_define_module_function(mGosu, "random", VALUEFUNC(_wrap_random), -1);
11677
11707
  rb_define_module_function(mGosu, "degrees_to_radians", VALUEFUNC(_wrap_degrees_to_radians), -1);
data/src/SndFile.hpp CHANGED
@@ -19,9 +19,8 @@ namespace Gosu
19
19
  Reader reader;
20
20
  Buffer buffer;
21
21
 
22
- // Cannot use /DELAYLOAD with libsndfile.dll because it was compiled
23
- // using arcane GNU tools of dark magic (or maybe it's the filename).
24
- #ifdef GOSU_IS_WIN
22
+ // /DELAYLOAD doesn't work with libsndfile.dll (is this still true?); manually lazy-load it.
23
+ #ifdef GOSU_IS_WIN
25
24
  static HMODULE dll()
26
25
  {
27
26
  static HMODULE dll = LoadLibrary(L"libsndfile.dll");
@@ -29,7 +28,7 @@ namespace Gosu
29
28
  return dll;
30
29
  }
31
30
 
32
- #define CREATE_STUB(NAME, RETURN, PARAMS, NAMES) \
31
+ #define CREATE_STUB(NAME, RETURN, PARAMS, NAMES) \
33
32
  static RETURN NAME PARAMS \
34
33
  { \
35
34
  typedef RETURN (__cdecl *NAME##_ptr) PARAMS; \
@@ -54,8 +53,8 @@ namespace Gosu
54
53
  CREATE_STUB(sf_strerror, const char*,
55
54
  (SNDFILE* sndfile),
56
55
  (sndfile))
57
- #undef CREATE_STUB
58
- #endif
56
+ #undef CREATE_STUB
57
+ #endif
59
58
 
60
59
  static sf_count_t get_filelen(SndFile* self)
61
60
  {
@@ -126,12 +125,12 @@ namespace Gosu
126
125
  info.format = 0;
127
126
  // TODO: Not sure if this is still necessary.
128
127
  // Can libsndfile open UTF-8 filenames on Windows?
129
- #ifdef GOSU_IS_WIN
128
+ #ifdef GOSU_IS_WIN
130
129
  load_file(buffer, filename);
131
130
  file = sf_open_virtual(io_interface(), SFM_READ, &info, this);
132
- #else
131
+ #else
133
132
  file = sf_open(filename.c_str(), SFM_READ, &info);
134
- #endif
133
+ #endif
135
134
  if (!file) {
136
135
  throw std::runtime_error(sf_strerror(nullptr));
137
136
  }
data/src/TextBuilder.cpp CHANGED
@@ -7,7 +7,7 @@ using namespace std;
7
7
 
8
8
  Gosu::WordInfo::WordInfo(const string& font_name, double font_height, vector<FormattedString> parts)
9
9
  {
10
- assert (! parts.empty());
10
+ assert (!parts.empty());
11
11
 
12
12
  auto* properties = utf8proc_get_property(parts.front().text.front());
13
13
 
@@ -21,7 +21,7 @@ Gosu::WordInfo::WordInfo(const string& font_name, double font_height, vector<For
21
21
 
22
22
  width = 0;
23
23
  for (const auto& part : parts) {
24
- assert (is_end_of_line || ! part.text.empty());
24
+ assert (is_end_of_line || !part.text.empty());
25
25
 
26
26
  width += text_width(part.text, font_name, font_height, part.flags);
27
27
  }
@@ -42,7 +42,7 @@ void Gosu::TextBuilder::flush_current_line(EndOfLineReason reason)
42
42
  if (current_line.back().is_whitespace) current_line.pop_back();
43
43
 
44
44
  // Shouldn't happen because the first word on a line should never be whitespace.
45
- assert (! current_line.empty());
45
+ assert (!current_line.empty());
46
46
 
47
47
  double words_width = 0, whitespace_width = 0;
48
48
  for (const auto& word : current_line) {
data/src/TrueTypeFont.cpp CHANGED
@@ -199,12 +199,13 @@ double Gosu::TrueTypeFont::draw_text(const u32string &text, double height,
199
199
  return pimpl->draw_text(text, true, height, bitmap, x, y, c);
200
200
  }
201
201
 
202
- bool Gosu::TrueTypeFont::verify_font_name(const unsigned char* ttf_data, const string& font_name, unsigned font_flags)
202
+ bool Gosu::TrueTypeFont::matches(const unsigned char* ttf_data,
203
+ const string& font_name, unsigned font_flags)
203
204
  {
204
- // Gosu's FontFlags enum mostly uses the same values as the STBTT_ macros.
205
+ // Gosu::FontFlags uses the same values as the STBTT_ macros, except for this one.
205
206
  int flags = (font_flags == 0 ? STBTT_MACSTYLE_NONE : font_flags);
206
207
 
207
- return stbtt_FindMatchingFont(ttf_data, font_name.c_str(), font_flags) >= 0 ||
208
+ return stbtt_FindMatchingFont(ttf_data, font_name.c_str(), flags) >= 0 ||
208
209
  stbtt_FindMatchingFont(ttf_data, font_name.c_str(), STBTT_MACSTYLE_DONTCARE) >= 0;
209
210
  }
210
211
 
@@ -248,7 +249,8 @@ Gosu::TrueTypeFont& Gosu::font_by_name(const string& font_name, unsigned font_fl
248
249
  if (font_name.find_first_of("./\\") != string::npos) {
249
250
  // A filename? Load it and add it to the stack.
250
251
  ttf_stack.push_back(ttf_data_from_file(font_name));
251
- } else if (font_name != default_font_name()) {
252
+ }
253
+ else if (font_name != default_font_name()) {
252
254
  // A font name? Add it to the stack, both with font_flags and without.
253
255
  ttf_stack.push_back(ttf_data_by_name(font_name, 0));
254
256
  ttf_stack.push_back(ttf_data_by_name(font_name, font_flags));
data/src/TrueTypeFont.hpp CHANGED
@@ -25,8 +25,8 @@ namespace Gosu
25
25
  Bitmap* bitmap, double x, double y, Color c);
26
26
 
27
27
  //! Returns true if the supplied buffer seems to be a font of the given name.
28
- static bool verify_font_name(const unsigned char* ttf_data,
29
- const std::string& font_name, unsigned font_flags);
28
+ static bool matches(const unsigned char* ttf_data,
29
+ const std::string& font_name, unsigned font_flags);
30
30
  };
31
31
 
32
32
  TrueTypeFont& font_by_name(const std::string& font_name, unsigned font_flags);
@@ -36,6 +36,19 @@ namespace Gosu
36
36
  //! In case of failure, this method must not return nullptr, but raise an exception.
37
37
  //! Note that this method does not accept any font flags, and so it will always load the first
38
38
  //! font in a TTC font collection.
39
+ //!
40
+ //! This function does not yet support Gosu::FontFlags, and consequently, custom TTF fonts do
41
+ //! not support markup or bold/italic text right now.
42
+ //!
43
+ //! Options for the future:
44
+ //! 1. Use stbtt_FindMatchingFont. This will only work for TTC font collections, and we will
45
+ //! have to patch stb_truetype to look for fonts only based on `int flags`, while ignoring
46
+ //! the name of fonts inside a bundle (who wants to deal with strings, anyway).
47
+ //! 2. Maybe Gosu should accept filename patterns like "LibreBaskerville-*.ttf" as the font
48
+ //! name and then replace the * with "Regular", "Bold", "Italic" etc.?
49
+ //! 3. As a last resort, Gosu could implement faux bold and faux italics. I think faux
50
+ //! underlines are a must anyway, since no font provides a dedicated TTF file for that.
51
+ //! These options are not mutually exclusive.
39
52
  const unsigned char* ttf_data_from_file(const std::string& filename);
40
53
 
41
54
  //! This method loads a TODO
@@ -46,22 +59,4 @@ namespace Gosu
46
59
  //! This method has a different implementation on each platform.
47
60
  //! In case of failure, this method must not return nullptr, but raise an exception.
48
61
  const unsigned char* ttf_fallback_data();
49
-
50
- // TODO still true? ↓
51
- // These functions do not yet support Gosu::FontFlags. This is fine for system fonts like Arial,
52
- // where the callers of these methods will typically load the correct file (e.g. ArialBold.ttf).
53
- // However, games which ship with their own font files (which is a good idea) can't use bold or
54
- // italic text using <b> or <i> markup because there is no way to associate one TTF file as the
55
- // "bold variant" of another.
56
- //
57
- // Options for the future:
58
- // 1. Use stbtt_FindMatchingFont. This will only work for TTC font collections, and we will
59
- // have to patch stb_truetype to look for fonts only based on `int flags`, while ignoring
60
- // the name of fonts inside a bundle (who wants to deal with strings, anyway).
61
- // 2. Maybe Gosu should accept filename patterns like "LibreBaskerville-*.ttf" as the font
62
- // name and then replace the * with "Regular", "Bold", "Italic" etc.?
63
- // 3. As a last resort, Gosu could implement faux bold and faux italics. I think faux
64
- // underlines are a must anyway, since no font provides a dedicated TTF file for that.
65
- // These options are not mutually exclusive.
66
-
67
62
  }