Compare commits

...

381 Commits

Author SHA1 Message Date
Tom Rothamel c12adecf9f Bump versions. 2013-06-26 08:05:53 -04:00
Tom Rothamel fc6013c5de Use the correct shader program for imageblends. 2013-06-26 07:57:29 -04:00
Tom Rothamel 3dd9f07fe2 Add changelog. 2013-06-26 07:53:04 -04:00
Tom Rothamel 5308fdd0b8 Skip sets proper values for config.skipping.
This:

* Displays the skip indicator.
* Allows for fast (to-next-menu) skipping.
* Fixes #97.
2013-06-24 22:37:49 -04:00
Tom Rothamel 273d1e296c Apply drag-drop fix. (Thank you, Kinsman.)
Fixes #99.
2013-06-24 22:24:10 -04:00
Tom Rothamel 152af7dc7b Changes to improve compatibility with very old Ren'Py games.
These were required to get the Ren'Py 5.0 demo to run. (You'll need
to create a script_version.rpy file set to (5, 0, 0) to get things
actually working.)
2013-06-20 17:58:15 -04:00
Tom Rothamel 43b34f22a3 Bump version. 2013-06-17 20:22:49 -04:00
Tom Rothamel 3330b451d0 Build on android-8. 2013-06-17 20:22:38 -04:00
Tom Rothamel 5290296952 Print the android savedir to stdout/logcat.
I just have the feeling this will come in handy one day.
2013-06-17 01:58:14 -04:00
Tom Rothamel 8fc784aef5 Place logs and traceback.txt into renpy.config.logdir.
On android, this is always placed on external storage, so we can
get to it.
2013-06-17 01:51:19 -04:00
Tom Rothamel 110c8b890b Change the search path for saves on android.
We look in:

* The public directory used by very old RAPT versions.
* The private directory used by newer RAPT versions.
* The getExternalFilesDir directory, used by the newest RAPT versions.

This means that saves will now be placed in a public place, so they
can be copied and otherwise shared.
2013-06-17 01:36:27 -04:00
Tom Rothamel b6c686d770 Do not looks for assets on the android filesystem.
In recent RAPTs we won't put anything there. What's worse, older games
might have left files there, which will interfere with our newer
code.
2013-06-17 00:07:54 -04:00
Tom Rothamel 6358db88ff Never invoke the editor on android. 2013-06-17 00:07:44 -04:00
Tom Rothamel b2611fd613 Use different shaders for clipping and no-clipping cases.
The clipping support in the shaders causes a noticable performance
hit on Android, and is rarely used. This fixes that in the common
case.
2013-06-16 22:25:46 -04:00
Tom Rothamel 5eb3af0188 Update android packaging docs to match the new RAPT. 2013-06-10 01:45:13 -04:00
Tom Rothamel ad71c17f77 Prevent import recursion that took place if utf-8 was not imported
before the Ren'Py loader was initialized.

This actually happend on Android.
2013-06-10 01:36:52 -04:00
Tom Rothamel 0d7dec8e4e Merge pull request #109 from JaHIY/master
add a variable set the minimum number of component in automatic_images
2013-06-07 20:26:29 -07:00
Tom Rothamel 112fbbfc08 Return the correct value from renpy.call_in_new_context. 2013-06-07 23:02:15 -04:00
JaHIY 4ee5240c72 change config.automatic_images_minimum_component to
config.automatic_images_minimum_components
2013-06-07 22:29:34 +08:00
JaHIY 025e359394 Now developers can set the minimum number of component 2013-06-07 20:04:31 +08:00
Tom Rothamel 143bcfad08 Enable minimize and quit shortcuts in the launcher. 2013-06-05 10:11:27 -04:00
Tom Rothamel a39ebd13e0 Check in the changelog. 2013-06-04 23:43:42 -04:00
Tom Rothamel e7c0a5f19f Check in problems.rst. 2013-06-03 23:26:46 -04:00
Tom Rothamel f0b03dc426 Add the Flatten displayable to Ren'Py. 2013-06-03 23:24:53 -04:00
Tom Rothamel 3d2ad79b6b Document additive blending. 2013-06-03 23:04:13 -04:00
Tom Rothamel 9f28a93c56 Deal with r.over in the sprite manager. 2013-06-03 23:00:25 -04:00
Tom Rothamel 386dd1d799 Deal (improperly) with additive blending in the software renderer.
When additive blending is 1.0, the software renderer will not
display an image. This is probably preferable to the image
displaying incorrectly, since they're likely to be effect
images.
2013-06-03 22:55:28 -04:00
Tom Rothamel e85ebf3a50 Add additive blending to Transform. 2013-06-03 22:30:05 -04:00
Tom Rothamel 81eb2e6f42 Additive blending in the GL renderer.
This commit adds in the backend support, for varying the additive
blending factor.

Additive blending will not be added to the (mostly obsolete) software
renderer. Instead, added a way to test if the current renderer
supports additive blending.
2013-06-03 22:11:29 -04:00
Tom Rothamel 9d021a2ec1 Bump version to 6.15.5. 2013-06-02 23:32:06 -04:00
Tom Rothamel 439fac65e2 Remove assert statements.
Thes won't run when optimization is enabled, which is the default
for Ren'Py. It's better to eliminate them entirely, so we have
the same configuration in testing and production.
2013-06-02 20:46:19 -04:00
Tom Rothamel dee484a16b Error message fix. 2013-06-02 16:57:16 -04:00
Tom Rothamel 90457d6f20 Fix translation problem.
Returned the wrong value when we used RENPY_UPDATE_STRINGS
to translate a string with a {#translation_comment} in it.
2013-06-02 13:42:16 -04:00
Tom Rothamel 245c30d7a7 Force font autohinting.
This was the behavior of of Ren'Py until switching to Freetype 2.4. The
autohinter more resembles the older Ren'Pys, seems to produce better
forms on most fonts, and fixes spacing issues on broken fonts.
2013-05-27 23:39:07 -04:00
Tom Rothamel 81b28293ca Allow * in classification patterns to match directories as well as
files.

Fixes #106.
2013-05-26 00:10:08 -04:00
Tom Rothamel 392f71b214 Use secure permissions in zip and tar files.
Previously, we used 666 and 777, and expected the user's umask to take
care of adjusting the permissions. Unfortunately, info-zip overrides
the umask, writing world-writable files to disk.

Thanks to Spiky Caterpillar for the bug and the fix.
2013-05-23 00:37:26 -04:00
Tom Rothamel 257a5281ed Make use of avresample optional.
This lets us compile on platforms that do not support it, using
the old audio_resample code.
2013-05-21 00:25:09 -04:00
Tom Rothamel d44f70a145 Revert "Update ffdecode.c to work with newer libav."
This reverts commit 1ffd4f97fc.
2013-05-21 00:00:45 -04:00
Tom Rothamel ec9198f25d Fix one more deprecation with newer libav. 2013-05-20 23:54:37 -04:00
Tom Rothamel 1ffd4f97fc Update ffdecode.c to work with newer libav.
This gets rid of deprecation warnings when building ffdecode.c, and
adds a dependency on the avsample library.
2013-05-20 23:28:14 -04:00
Tom Rothamel e8968252ab Skip whitespace before default parameters and arguments.
Fixes #101.
2013-05-06 22:58:34 -04:00
Tom Rothamel ae618ddd76 Allow the creator to cancel out of create file and generate translation.
Fixes #100.
2013-05-04 12:24:30 -04:00
Tom Rothamel 005aa1a64c Read assets from an expansion file if one is available on Android. 2013-04-14 22:51:55 -04:00
Tom Rothamel a63231d36f Merge branch 'master' into android 2013-04-13 23:06:26 -04:00
Tom Rothamel db530f7def Document config.start_callbacks. 2013-04-07 20:00:47 -04:00
Tom Rothamel 627261f51d Clarify what the Ren'Py license applies to. 2013-04-07 19:43:58 -04:00
Tom Rothamel d080b71f29 Make exclude_empty_directories deal with non-terminal directories. 2013-04-07 11:13:00 -04:00
Tom Rothamel 0a62195625 Update changelog, document problems. 2013-04-06 23:11:51 -04:00
Tom Rothamel 571bb43459 Bump version to 6.15.4. 2013-04-06 23:00:10 -04:00
Tom Rothamel 817b9d5c9f Fix race condition in the updater.
If the update finished while we were checking progress, zsync
wouldn't rename the file and the update would fail.
2013-04-06 22:55:30 -04:00
Tom Rothamel 6f3fa9bf8d Make loads after rollback non-greedy.
A greedy load after rollback can roll-back through with statements,
which are turned into with None statements until the start of the
next interaction. (Which is sensible behavior for the rollback
case, but fails when an after_load transition is scheduled.)

Fixes #95.
2013-04-06 21:13:47 -04:00
Tom Rothamel 2de5a7d9f7 Add build.exclude_empty_directories variable. 2013-04-06 20:58:10 -04:00
Tom Rothamel d26f1a65e8 Add the ability to include empty directories to the zip format.
Per http://lemmasoft.renai.us/forums/viewtopic.php?f=32&t=2023
2013-04-06 18:31:59 -04:00
Tom Rothamel 667c2e59d3 Do not set the window size while entering/leaving fullscreen mode. 2013-04-06 17:52:12 -04:00
Tom Rothamel b4411a027f Fix documentation issues.
Fixes #89.
2013-04-06 13:53:20 -04:00
Tom Rothamel 142831ec44 Remove duplicated docs.
Fixes #90.
2013-04-06 13:19:48 -04:00
Tom Rothamel 2dd58759de Remove extraneous debug print. 2013-04-04 19:06:55 -04:00
Tom Rothamel 866f3a11b2 Use nogil_copy to copy surfaces.
Previously, we used the angle copy function to copy surfaces, which
used the pygame blit function. The pygame blit function was more
complex and held the GIL while blitting, and led to slowdowns in
loading.

Nogil_copy appears to be safe for Ren'Py, which doesn't use software
surfaces.
2013-04-04 12:13:56 -04:00
Tom Rothamel 8a2d779320 Spend less time with the image cache locked.
The image cache is no longer locked for expensive operations,
such as image loads. While this may cause us to load an image
twice (in rare circumstances), it also lets us preload images
without blocking loads frome the main thread.
2013-04-04 03:22:36 -04:00
Tom Rothamel 2ef8cd4c3f Remove obsolete renpy_font module. 2013-04-02 23:42:21 -04:00
Tom Rothamel 88ec1e66e6 Use rwobject.c that's included in pygame, rather than building
our own.
2013-04-02 23:40:32 -04:00
Tom Rothamel f6bfeefc8e Merge branch 'master' into android 2013-04-02 20:05:55 -04:00
Tom Rothamel bbdf9023fe Check in tkaskdir. 2013-04-02 20:05:34 -04:00
Tom Rothamel b0070122f8 Merge branch 'master' into android 2013-03-31 21:42:49 -04:00
Tom Rothamel ad51bfb30f Kick off preload thread at the start of image prediction, rather
than the end of an interaction.

Kicking it off at the end of the interaction lead to bad behavior
where the start of the next interaction would fight with the
preload thread for the image cache lock.
2013-03-31 17:56:16 -04:00
Tom Rothamel d15ae51825 New add.py script that combines the old add.sh and tag.py scripts,
and also does the dl symlinking.
2013-03-30 12:51:03 -04:00
Tom Rothamel 2efa265983 Bump version. 2013-03-30 11:50:21 -04:00
Tom Rothamel cc6513c93a Paranoia regarding cases where Transform.event is called before
Transform.render.

Closes #87.
2013-03-29 23:40:41 -04:00
Tom Rothamel a81865484c Ensure that we render displayables before passing events to them,
even if video is playing.
2013-03-29 23:34:11 -04:00
Tom Rothamel d70eb13cb0 Wait until a frame is available before drawing the screen while
displaying full-screen video.

Fixes #85.
2013-03-29 23:18:00 -04:00
Tom Rothamel 4b5d94eeb9 Re-offset a viewport if its size changes before the first event has
been processed.

This is necessary to deal with the way a size may offer the viewport
a smaller area than initially necessary, once scrollbars take up
some space.

Fixes #88.
2013-03-29 20:32:51 -04:00
Tom Rothamel 5674a4ddac Produce an error when dealing with logical lines longer than 64K
characters in size.

Line parsing can be N**2, so this turns a really slow error into one
that takes a reasonable amount of time.
2013-03-29 00:28:13 -04:00
Tom Rothamel 5bb6483ab4 When a page is given specified to FileSave, use it to determine
if the action should be sensitive.

This was preventing QuickSave from working when the default file page
was set to auto.

Fixes #82.
2013-03-29 00:15:14 -04:00
Tom Rothamel f62e4b6eb7 Add more protection to pygame.event.post statements.
Fixes #86.
2013-03-28 23:46:30 -04:00
Tom Rothamel c54a613393 Bars only run hover and unhover actions once. 2013-03-28 22:57:12 -04:00
Tom Rothamel 2c4be00267 Ensure image fonts include U+200B, since we need that for
input to work.
2013-03-27 21:04:29 -04:00
Tom Rothamel 0b8c22e0d8 Sort SpriteManager sprites by their zorder.
Thanks to Kinsman for the fix.
2013-03-27 10:11:45 -04:00
Tom Rothamel 65a7dca95f Fix random segfaulting related to TTF lookup tables, especially on shift+R.
The segfaulting occured when a TTF Font contains a GSUB table with a
Lookup type other than one. This caused us to initialize a data
structure that represents the lookup table, but never fill it in.

Rarely, one of these tables would hava field initalized to 1 or 2. In
that case, we'd try to free associated data that was never properly
initialized, and we'd crash.

The fix was to intialize the data structures before use.
2013-03-27 00:06:48 -04:00
Tom Rothamel fdcfaface0 Do inexpensive prediction while displaying frames.
This involved breaking the prediction coroutine, and the context
prediction routine up further. By predicting one statement at a time,
we can interleve predicitions with frame display, and prevent
dissolves or ATL transforms from starving the image predictor, at
least in simple (non-screen) cases.
2013-03-26 00:58:33 -04:00
Tom Rothamel 5f08884b52 Predict images used in ATL transforms that are part of show/scene
statements.
2013-03-25 23:36:46 -04:00
Tom Rothamel 3595eee11c Prevent memory leaks via the rollback log.
Before this change, code that created and then immediately modified a
list, dict, set, or object that was rollback-able would cause a small
memory leak until the end of the interaction.
2013-03-24 23:51:13 -04:00
Tom Rothamel b33eb54c34 Bound the layout cache size.
This helps ensure that code that creates many text objects in a
single interaction will now consume unbounded memory if it lets
those Text objects be GCed.
2013-03-24 23:49:02 -04:00
Tom Rothamel c86fc743bc Change memory profile to show the differen between the number of
objects at each call.
2013-03-24 23:48:39 -04:00
Tom Rothamel 10b82603b9 Do not translate the who-prefix or who-suffix if new-style
subsititutions are disabled.
2013-03-23 20:13:04 -04:00
Tom Rothamel 1a1ae36299 Quit the display when doing a forced resize (like when clicking the
Window preference.)

This prevents the possibility of spurious resize events, and also
prevents the black bars from appearing on the screen after such
a resize.
2013-03-20 23:26:36 -04:00
Tom Rothamel cc004c00f0 Merge branch 'master' into android 2013-03-19 23:30:51 -04:00
Tom Rothamel cda96afd14 Ensure an over-full video queue can't starve the audio queue,
and vice-versa.
2013-03-19 00:04:18 -04:00
Tom Rothamel 966630ea16 Write .zip files out in large chunks.
Previously, we would load entire files into RAM, compress the entire
file, and write out the compressed data. This was breaking down on
large .rpas, hence the change.
2013-03-17 18:42:19 -04:00
Tom Rothamel 8c746bdccc Work around fonts that don't have the ZWSP (U+200b) character.
We use ZWSP in input so we properly size an empty line. Some fonts
do not include this character, and replace it with an undefined
character glyph (often a square).

This fix prevents Ren'Py from rendering the ZWSP, making the
problem invisible to the user.
2013-03-16 22:49:34 -04:00
Tom Rothamel 4fac7da3bb Start work on documenting the python statements. 2013-03-10 23:32:44 -04:00
Tom Rothamel 4045958cd2 Bump version. 2013-03-09 23:11:51 -05:00
Tom Rothamel 45155a9ff4 Use python+tkinter to prompt for the projects directory.
Fixes #77.
2013-03-09 23:02:21 -05:00
Tom Rothamel 83ad789654 Improve OpenGL ES2 support. 2013-03-08 23:44:10 -05:00
Tom Rothamel 21eaa5ee9c Merge branch 'master' into android 2013-03-08 21:31:50 -05:00
Tom Rothamel 69bcd84d5d Fix doc typos.
Thanks, Koichi.
2013-03-08 00:57:07 -05:00
Tom Rothamel 7357fa4c5b Allow the music room to loop tracks, rather than advancing them. 2013-03-07 11:38:32 -05:00
Tom Rothamel d51c768112 Document renpy.input. 2013-03-06 22:44:28 -05:00
Tom Rothamel 32ae4fef3a Bump version. 2013-03-04 10:09:20 -05:00
Tom Rothamel fd5ee77f1c Force use of shaders on android.
The auto-detect code fails on ES.
2013-03-04 01:15:56 -05:00
Tom Rothamel b9acbfccd7 Changes required to support android.
The new android support uses GL2, so we need to change some defines
to use it.
2013-03-04 00:00:08 -05:00
Tom Rothamel d05059e64d Stop ongoing transitions when we enter a new context.
A minor performance improvement, which also prevents the side-effects
of the new code being surrounded by old transitions.
2013-03-03 01:08:10 -05:00
Tom Rothamel bc80e38bb5 Change how we compensate for times in a restarted move transition.
The previous method was causing problems where when we created
a new screen under a transition, the new screen would get the
wrong creation time.
2013-03-03 00:48:29 -05:00
Tom Rothamel 838233a68f Doc fix. 2013-03-02 21:27:35 -05:00
Tom Rothamel 6968d973a6 Fix compatibility between OldMoveTransition and define.move_transitions.
Fixes #74.
2013-03-02 12:28:53 -05:00
Tom Rothamel 199f6aa70c Include the language as part of a rollback copy.
This prevents us from running the language change code whenever we
do a rollback when a non-default language is enabled.

Fixes #73.
2013-02-28 22:30:29 -05:00
Tom Rothamel b7e33bb2b9 Documentation fix. 2013-02-28 00:01:44 -05:00
Tom Rothamel d2d777be0c Doc fix. 2013-02-27 23:59:49 -05:00
Tom Rothamel 4c924b4971 Documentation fix. 2013-02-27 23:55:41 -05:00
Tom Rothamel ddd4a1a4ea Fix mistake. 2013-02-27 23:54:57 -05:00
Tom Rothamel c68f240061 Fix more launch options. 2013-02-25 09:42:49 -05:00
Tom Rothamel 5d9d4955ff Allow symlinks to renpy.py. 2013-02-25 09:26:35 -05:00
Tom Rothamel fb4eeaaaeb Update sphinx docs. 2013-02-24 22:51:49 -05:00
Tom Rothamel a0b04c2d99 Allow ffdecode.c to compile with ffmpeg.
Patch thanks to Maks Verver.
2013-02-24 22:32:28 -05:00
Tom Rothamel 86b38b5e40 Add load and save commands to the console. 2013-02-23 22:04:14 -05:00
Tom Rothamel 2e6ff091e2 Implement config.console_callback. 2013-02-23 21:42:36 -05:00
Tom Rothamel e91972589c Change the console activation key to shift+O only.
The backtick key invokes IMEs, which can confuse Ren'Py.
2013-02-23 21:26:05 -05:00
Tom Rothamel eb5a8b8beb Fix translation documentation problems reported by Koichi. 2013-02-22 23:55:15 -05:00
Tom Rothamel e1320c4661 Restart interaction on FieldValue change. Add VariableValue. 2013-02-21 23:57:05 -05:00
Tom Rothamel 2db0d397a6 Add a README that shows up on github. 2013-02-21 23:05:30 -05:00
Tom Rothamel 7f85af9c54 The after checkout script can now copy the files necessary to run
Ren'Py from a built Ren'Py.
2013-02-21 22:23:45 -05:00
Tom Rothamel 3a981be8c6 Delete obsolete scripts. 2013-02-21 21:01:18 -05:00
Tom Rothamel 95b51dae6f Style _default for the Japanese translation. 2013-02-20 23:01:59 -05:00
Tom Rothamel 9eef533e8a Edit all no longer opens TL files. 2013-02-20 22:43:05 -05:00
Tom Rothamel 8e4930d031 Run python with -E flag.
This ignores a PYTHONPATH the user has lying around for some other reason.
2013-02-20 22:40:31 -05:00
Tom Rothamel e398518bd1 Do not modify the meta path on shift+R. 2013-02-19 23:54:32 -05:00
Tom Rothamel 48bdab0ed5 Don't use non-existent field in console. 2013-02-19 23:50:28 -05:00
Tom Rothamel ab122f92ad Allow the user to type { and } at the console. 2013-02-19 23:40:43 -05:00
Tom Rothamel ef0b374959 Fix space quoting in renpy.sh. 2013-02-19 22:29:36 -05:00
Tom Rothamel dbfc04afc4 If pythonw doesn't exist, launch with sys.executable. 2013-02-18 23:00:51 -05:00
Koichi Akabe 7d6687f46f update Japanese translation 2013-02-18 22:35:20 -05:00
Tom Rothamel f8483ec22e Add shift+O as a way of invoking the console. 2013-02-18 22:01:23 -05:00
Tom Rothamel 0b690bbe43 Add the __ function, which immediately translates a string. 2013-02-18 21:25:45 -05:00
Tom Rothamel 871aa08f41 Ensure that _restart is set, even if we restart at the main menu. 2013-02-18 21:07:03 -05:00
Tom Rothamel e2c2e2e841 Defer the import of pygame.macosx.
This seems necessary to get it to import on OS X 10.6.8.
2013-02-18 19:32:19 -05:00
Tom Rothamel ba9dfa8f4c Make the translation code unicode-safe.
Also, put a TODO before each translation.
2013-02-17 22:11:13 -05:00
Tom Rothamel cad5b436e5 Doc update. 2013-02-17 18:55:11 -05:00
Tom Rothamel 312fdcea96 Remove Ren'Py 7 mention from tutorial; add edgescroll demo. 2013-02-17 17:51:10 -05:00
Tom Rothamel 518a969aab Fix bug in edgescroll that prevented scrolling from happening while the mouse was being moved. 2013-02-17 17:50:47 -05:00
Tom Rothamel e6687bd7b8 Run zsync in the update directory.
At least on mac, it seems to want to write files to the current
directory. By default that's the root directory, and hence
problematic.
2013-02-17 14:24:51 -05:00
Tom Rothamel 74260589f4 Do not package debug output. 2013-02-17 13:51:23 -05:00
Tom Rothamel aefd6e6a9e Tweak mac and windows /lib build patterns. 2013-02-17 11:44:50 -05:00
Tom Rothamel 58e60ee148 Fix editra build rules. 2013-02-17 01:28:49 -05:00
Tom Rothamel fdc2b21e0b When the RSA module is missing, do not offer an update. 2013-02-17 00:20:19 -05:00
Tom Rothamel 9d6c15e10b Send stdout and stderr to null when we don't have stdout or stderr.
This is the case when running on windows under pythonw (the default).
2013-02-16 22:48:27 -05:00
Tom Rothamel f271d3890b Use python.exe to launch projects on windows. 2013-02-16 21:11:07 -05:00
Tom Rothamel 69a4a29014 Remove obsolete windows build files. 2013-02-16 12:39:30 -05:00
Tom Rothamel f72771c026 Write lint.txt in ascii mode, so the files we produce can be read
on windows.
2013-02-15 21:47:10 -05:00
Tom Rothamel d2e05c481f Change how we define eruby_ja.
This lets it be linted properly.
2013-02-15 21:42:53 -05:00
Tom Rothamel f999cfc2c7 Translate the date format string into Japanese. 2013-02-15 01:02:20 -05:00
Tom Rothamel 6c7812b8a4 Remove locale support.
It didn't work on windows, since windows locales work using the ANSI
code page, and there's no way to get them to use UTF-8 or unicode.
(Also, having multiple locale names is asking for problems.)
2013-02-15 00:55:06 -05:00
Tom Rothamel b841358e22 Merge branch 'fix-error-ruby-ja' of https://github.com/vbkaisetsu/renpy into devel 2013-02-15 00:28:51 -05:00
Koichi Akabe 3419343e85 fix errors around ruby 2013-02-15 13:40:00 +09:00
Tom Rothamel 917fc68e27 Document the new mac requirements. 2013-02-14 23:28:53 -05:00
Tom Rothamel fadd0279a8 Add the optimize option to the distribute command line. 2013-02-14 23:28:37 -05:00
Tom Rothamel bbcc75dd2d More changelog updates. 2013-02-14 22:55:17 -05:00
Tom Rothamel f0b44d8c02 Update changelog. 2013-02-13 23:34:35 -05:00
Tom Rothamel eddf89697d Add the renpy.end_interaction function.
This creates an exception that ends the current interaction. This can
be used in the render and event methods of a creator-defined
displayable.
2013-02-13 22:00:09 -05:00
Tom Rothamel 92a155c780 Implement the renpy.set_locale method, which translation use to set the locale.
Modify the japanese translation of the tutorial to use it.
2013-02-13 15:51:56 -05:00
Tom Rothamel 6661e6e058 As part of a new approach to locales, rename back to japanese. 2013-02-13 15:08:06 -05:00
Tom Rothamel 68c49a9aa1 Merge pull request #67 from vbkaisetsu/i18n-confirm-n-locale
Please merge i18n-confirm-n-locale
2013-02-13 10:59:52 -08:00
Koichi Akabe 1ae7d4bd54 update font glyphs
The following is new glyph: U+672A
2013-02-13 17:25:25 +09:00
Koichi Akabe 8330ef062e update translation to support changing locale
fix font location
2013-02-13 16:55:53 +09:00
Koichi Akabe 5493ba998e change the locale when the language is changed 2013-02-13 16:35:11 +09:00
Koichi Akabe ed59d8a351 update Japanese translation 2013-02-13 13:38:00 +09:00
Koichi Akabe 077def41ae use _ function for confirmation messages 2013-02-13 13:32:54 +09:00
Tom Rothamel 7480a33eaf Make the file slot info translatable. 2013-02-12 17:59:18 -05:00
Tom Rothamel 4e1e8832cc Allow for tutorial-only sections of screens.rpy.
We generate template/game/screens.rpy from tutorial/game/screens.rpy -
but now, the tutorial version has the language preference in it. We don't
want the language preference to be in the default template, so we use
distribute.py to filter it out.
2013-02-12 17:51:15 -05:00
Tom Rothamel fe290a42c1 Do not distribute the launcher/game/themes directory.
This doesn't exist in git, but I use it to test the launcher theming
support - eliminating it means I don't have to remember to delete
the directory.
2013-02-12 17:37:57 -05:00
Tom Rothamel 54ae627ed2 Improve lint to count words and characters by language, and to count images and screens. 2013-02-12 16:59:10 -05:00
Tom Rothamel a3d30e9012 Update the VL-Gothic license file to include all relevant info. 2013-02-12 12:35:00 -05:00
Tom Rothamel 021e9c6335 Move VL-Gothic files into tl/japanese 2013-02-12 12:34:45 -05:00
Tom Rothamel 3833f01f2d Merge pull request #58 from vbkaisetsu/tutorial-i18n-l10n-ja
Merge japanese translation of tutorial game.
2013-02-12 06:50:22 -08:00
Tom Rothamel 2060dc288e Remove obsolete voice readme file. 2013-02-12 09:48:46 -05:00
Tom Rothamel fe10a1d57e Voice documentation. 2013-02-12 09:34:56 -05:00
Tom Rothamel 563e50334c Various voice actions:
* SetVoiceMute - sets the mute of a voice tag.
* ToggleVoiceMute - toggles the mute of a voice tag.
* VoiceReplay - replays the current voice.
2013-02-12 00:41:05 -05:00
Tom Rothamel a630c1daa4 Adjust indentation level. 2013-02-12 00:24:40 -05:00
Tom Rothamel 97380ceae6 Bring the voice mute code to the point where it works. 2013-02-12 00:20:34 -05:00
Tom Rothamel 7f8a4ec63d Various improvments to the voice mute support that was
just merged in.
2013-02-12 00:03:28 -05:00
Tom Rothamel 88eaf9ad53 Merge branch 'voice_enhancement' of git://github.com/odysseystudio/renpy into devel 2013-02-11 23:36:18 -05:00
Koichi Akabe 0e8a7f4b30 add license information for VLGothic 2013-02-11 23:59:46 +09:00
Koichi Akabe 42cd424723 add light-weight font based on VL Gothic 2013-02-11 22:53:55 +09:00
Tom Rothamel d7319907e8 Update the copyright notices to reflect the new year. 2013-02-10 21:41:51 -05:00
Tom Rothamel 3c942cfea1 Rename release. 2013-02-10 21:15:40 -05:00
Tom Rothamel 2884b93971 For clarity's sake, import time from distribute.rpy. 2013-02-10 07:41:36 -05:00
Tom Rothamel 494f445b33 Also call the before_restart code on load.
For the same reasons.
2013-02-09 23:57:16 -05:00
Tom Rothamel f23299fc9a Prevent old screens from updating while the game is restarting.
This prevents an issue that was triggered by an old screen hanging
around during a game restart, but that screen using a variable that
is no longer defined after game start.
2013-02-09 23:45:10 -05:00
Tom Rothamel d53f00a55a Remove incorrect uses of NotImplemented.
Which isn't an exception - it's a return value.
2013-02-08 22:38:40 -05:00
Tom Rothamel 89315c90e8 Wrap a list's rmul operation.
This fixes #65, a bug where [1, 2, 3] * 4 would produce a RevertableList,
but 4 * [1, 2, 3] would produce an unwrapped list (that doesn't
participare in rollback).
2013-02-08 22:32:26 -05:00
Tom Rothamel 3fedc41772 Rename the pythonw executable to use executable_name.
This has the nice behavior that the game we're running winds up being
called <game>, rather than python.
2013-02-06 22:56:30 -05:00
Tom Rothamel da4db2e4a2 Moves/fixes to work with new windows packaging. 2013-02-03 23:25:12 -05:00
Tom Rothamel 7e0f611279 Build mac apps + distribution.
A bunch of small changes, but the big one here is that we now generate
the mac app from scratch, using the plist creation code that was
checked in earlier.
2013-02-02 23:41:58 -05:00
Tom Rothamel 56ff162681 Search for Ren'Py when launched from a mac app. 2013-02-02 00:46:31 -05:00
Tom Rothamel 0eebc91e3d Fix renpy.sh so it works. 2013-02-01 23:28:41 -05:00
Tom Rothamel 284dc98394 Improvements to mac support.
* Write out a correct Info.plist
* Allow renpy.sh to call the new darwin build.
* Call the argv emulator.
2013-02-01 01:55:43 -05:00
Tom Rothamel f11928f91f Basic info.plist writer. 2013-01-30 23:56:50 -05:00
Tom Rothamel 1bcbdc1411 Add viewport edge scrolling.
This is a mode where the viewport automatically scrolls when the mouse
approaches the viewport's edges.

Fixes #17.
2013-01-29 23:26:36 -05:00
Tom Rothamel 19067d10ea Fix last commit. 2013-01-29 17:38:00 -05:00
Tom Rothamel e53c9f596b Make nvl hide and nvl show switch the mode used by window hide and
window show.

Since otherwise, it looks bad.
2013-01-29 17:22:23 -05:00
Tom Rothamel 970420e275 Make key repeat configurable.
Also, use the compat mechanism to disable it for old games, since
there's a small-but-not-zero chance it could break something.
2013-01-27 11:07:16 -05:00
Tom Rothamel 9d3d2877d9 Remove unnecessary code from renpy.sh. 2013-01-26 23:34:25 -05:00
Tom Rothamel 32af9095cb Add the transform_around style property.
Fixes #63.
2013-01-26 00:22:04 -05:00
Tom Rothamel ea6304f4bc Implement the guts of the transform_anchor transform property.
This transforms the anchor using the same matrix as the rest of
the transform, essentially allowing the point around which the
image rotates/zooms to be specified.
2013-01-26 00:02:26 -05:00
Tom Rothamel 695a804813 Tweak side image attribute handling, so it works w/ menus.
Fixes #59.
2013-01-24 11:18:40 -05:00
Tom Rothamel 88d23f22a4 Prevent the time inside an ATL transform from ticking backwards.
This could happy in various cases, such as when the show and hide
operations were called. The result is that the time inside an
interpolate could go massively negative, and we'd get overly-long
delays and other artifacts.
2013-01-24 00:49:00 -05:00
Tom Rothamel 754b6dc069 Don't invert the bar keys when bar_invert is true.
(We already inverted the value, so inverting the keys is redundant.)
2013-01-23 19:39:15 -05:00
Tom Rothamel b72466a1e3 Set keys to repeat. 2013-01-23 19:24:57 -05:00
Tom Rothamel d7d7795b99 Fix a regression that prevented load from working. 2013-01-23 19:17:03 -05:00
Tom Rothamel f4c03d58c9 Force tar times to be integers. 2013-01-22 13:44:03 -05:00
Tom Rothamel 431231ed68 Ensure that the start and end segments of Text are always included
in the list of segments that are shown to the user.

Fixes #60.
2013-01-21 14:59:51 -05:00
Tom Rothamel 34521e8656 Fix mmx bug on mac; remove obsolete script. 2013-01-20 23:47:25 -05:00
Koichi Akabe 16765ff731 Fix wrong translation 2013-01-20 23:11:33 +09:00
Koichi Akabe 2e59973c8d Fix wrong translation 2013-01-20 22:58:37 +09:00
Koichi Akabe e0277b2b59 Add style for translation 2013-01-20 22:12:21 +09:00
Koichi Akabe 91ddb05f27 Add Japanese translation 2013-01-20 21:44:17 +09:00
Koichi Akabe 70b215054d Use a map instead of str.lower() for translation 2013-01-20 21:08:58 +09:00
Koichi Akabe afe596eb7d Use _ function to support i18n in tutorial game 2013-01-20 17:30:06 +09:00
Tom Rothamel 811f04e783 Add Yes/No menu transitions. 2013-01-18 11:25:04 -05:00
Tom Rothamel 38543b0979 More documentation improvements. 2013-01-17 23:54:13 -05:00
Tom Rothamel 0574b03aeb Update changelog to reflect 6.15 work. 2013-01-17 23:31:47 -05:00
Tom Rothamel 460ccfcacb Call slow_done if a character is called with slow text disabled.
This triggers the slow text callback, which might be needed to end
something called in one of the other callbacks. (For example, the
PW-style beeping speech.)
2013-01-16 22:53:27 -05:00
Tom Rothamel 50e0f04b9a Remove warnings. 2013-01-16 11:13:04 -05:00
Tom Rothamel 77bc0f095f Remove eclipse warnings. 2013-01-16 11:12:42 -05:00
Tom Rothamel 064e7f544a Make code match the docs. 2013-01-16 01:25:24 -05:00
Tom Rothamel 06f329ae3b Document tailoring. 2013-01-16 01:23:33 -05:00
Tom Rothamel d05283200b Add the renpy.language_tailor function.
This lets the user tailor the linebreaking algorithm even further by
changing the unicode line breaking classes of individual characters.
2013-01-16 01:21:47 -05:00
Tom Rothamel d700c3ea17 Linebreaking improvements.
* Update the linebreaking to the algorithm and tables in unicode 6.2.
* Implement japanese-strict, japanese-normal, and japanese-loose breaking methods.
2013-01-16 00:47:55 -05:00
Tom Rothamel 3c381320c0 Properly style input screen. 2013-01-14 20:45:33 -05:00
Tom Rothamel 876d07f467 Import the decimal module, so it's included w/ Ren'Py. 2013-01-14 18:18:54 -05:00
Tom Rothamel 691adcb82d Expose the child style property to the screen language.
It was exposed before, but not as a style property. So it didn't work
as well as it could have.
2013-01-13 23:58:08 -05:00
Tom Rothamel 0321b19eaa Move box_reverse and order_reverse into the styles.
This makes it at least moderately plausible that a translation can
change them.
2013-01-13 23:42:31 -05:00
Tom Rothamel 02a84d71a6 Swap the width and height when rendering vertical text. 2013-01-13 22:03:03 -05:00
Tom Rothamel 504a81b86a Remove changelog from git. 2013-01-13 21:03:47 -05:00
Tom Rothamel 0baf603f90 Remove dse from Ren'Py repository. 2013-01-13 14:33:43 -05:00
Tom Rothamel 7593b7a3e3 Remove renpygame from renpy repo. 2013-01-13 14:32:56 -05:00
Tom Rothamel b07a146f27 Dlc test script. 2013-01-13 14:25:37 -05:00
Tom Rothamel 2eee59c530 Add back support for .rpt translation files. 2013-01-13 14:22:11 -05:00
Tom Rothamel 9b50b5bdb5 Translation generation improvements:
* Include commented code as part of the generated translations.
* Generate empty translation statements.
2013-01-13 13:38:11 -05:00
Tom Rothamel 4911348396 Implement the {#...} text tag, to distinguish translatable strings. 2013-01-12 22:32:42 -05:00
Tom Rothamel 2a9759e7d2 Fix a regression that caused us to constantly queue up redraw events. 2013-01-12 22:09:06 -05:00
Tom Rothamel 4366e29c05 ATL xoffset and yoffset now take floats.
Also, document them.
2013-01-12 20:55:39 -05:00
Tom Rothamel c612eef65d Do not try to execute python early code that won't compile.
This prevents a Ren'Py error from masking the original syntax error.
2013-01-12 12:41:51 -05:00
Tom Rothamel 48cd830628 Recompute scene list index after _hide.
Closes #45.
2013-01-11 12:47:54 -05:00
Tom Rothamel 7be08a2e2d renpy.loadable should check in .apk packages. 2013-01-10 22:17:56 -05:00
Tom Rothamel 3d4def2072 Prevent a widget from being grabbed if it's part of a screen
that is being hidden.
2013-01-10 21:57:45 -05:00
Tom Rothamel 259ef699d4 Make bar key bindings reflect bar_invert and bar_vertical. 2013-01-10 20:27:44 -05:00
Tom Rothamel 2a79d00726 Fix screen call/show without arguments. 2013-01-10 20:14:38 -05:00
Tom Rothamel eb27efd9e0 Allow the screen language use statement to pass positional parameters. 2013-01-10 00:31:57 -05:00
Tom Rothamel c94ce0ac7d Screens accept parameters via _args and _kwargs.
Calls to screens supply parameters via those variables.
2013-01-10 00:24:07 -05:00
Tom Rothamel 0acd9a38fa Allow screen language screens to accept parameters. 2013-01-10 00:00:39 -05:00
Tom Rothamel 5a0a60e8ba Factor parameter acceptance code into its own function. 2013-01-09 23:57:55 -05:00
Tom Rothamel 02854ef0a0 Deal with meaningless code from older games that could cause a crash
in 6.14.
2013-01-08 10:28:40 -05:00
Tom Rothamel 62e5b63229 Remove launcher2. 2013-01-07 23:57:20 -05:00
Tom Rothamel c2c537baa2 Add the "Add script file" command to the launcher. 2013-01-07 23:56:45 -05:00
Tom Rothamel 89223eb90f Document launcher skinning. 2013-01-06 23:08:49 -05:00
Tom Rothamel 2ba95ea13b Generate checkbuttons from colors in theme. 2013-01-06 22:11:05 -05:00
Tom Rothamel e4af56c5ae launcher: Add basic themeing support. 2013-01-06 21:54:50 -05:00
Tom Rothamel 5accf191dc If we can't create the tmp directory, use tempfile to make one. 2013-01-06 18:41:56 -05:00
Tom Rothamel a011e15e14 Implement the japanese-alternate language.
This breaks before and after every wide character, which emulates
the algorithm used by Japanese visual novels.

This was discussed at:
http://lemmasoft.renai.us/forums/viewtopic.php?p=242978#p242978
2013-01-06 13:55:15 -05:00
Tom Rothamel 2c9b67c87e Don't use vertical in font_replacement_map, as it would be a
non-backwards-compatible change.
2013-01-05 22:46:48 -05:00
Tom Rothamel 895c8f2dde Document vertical text. 2013-01-05 22:43:24 -05:00
Tom Rothamel 47e5f54776 Document box_reverse and order_reverse. 2013-01-05 22:21:00 -05:00
Tom Rothamel 13ff44ec32 Add order_reverse to vbox, hbox, and fixed.
This reverses the order in which child displayables are blitted to
the screen.
2013-01-05 22:02:09 -05:00
Tom Rothamel 8d03df8553 Implement box_reverse.
This reverses the order of an hbox or vbox's children. For example, in
a box_reversed hbox, the first child will be rightmost, and the last
child will be leftmost.
2013-01-05 21:21:00 -05:00
Tom Rothamel 5bdac85eab Vertical text integration.
Vertical text is now automatically rotated to the vertical
orientation.

Add the vtext image and vcentered character, which are the
vertical-text equivalents of image text and centered.
2013-01-05 14:15:38 -05:00
Tom Rothamel f391f2d9c6 Merge branch 'master' of git://github.com/vbkaisetsu/renpy into devel
This adds vertical text support.
2013-01-05 11:10:14 -05:00
Tom Rothamel ebebd6c6b9 Update translation documentation. 2013-01-05 00:10:30 -05:00
Tom Rothamel e7b4a1d156 Merge remote-tracking branch 'ren/patch-5' into devel 2013-01-04 22:24:32 -05:00
Tom Rothamel a93d083cce Change the comments generated by the translator. 2013-01-03 23:50:25 -05:00
Tom Rothamel c38d6c07f7 Change the identifier generation scheme.
It now includes the label, and will generate a unique identifier for
the repetitions of code within a label.
2013-01-03 23:45:01 -05:00
Tom Rothamel 0d1a6f1926 Add hide clause to parsing of Label.
The hide clause will be used with translations, to avoid taking the
label name into account when generating the translation identifier.
2013-01-02 23:48:58 -05:00
Tom Rothamel b6e86ec2f2 Translate only a single line of dialogue at a time. 2013-01-02 23:38:02 -05:00
Tom Rothamel d867e5a578 Transform fixes:
* Taking execution state now works with nested Transforms.
* An ATL transform will take the child of the Transform it replaces,
  if it has no child of its own.
2013-01-02 22:08:34 -05:00
Tom Rothamel 8fd52a383e Fix a bug where the cache can grow uncontrollably.
This error hits when the cache is over-full - we used to bail out
early, and never call the cleanout code. This fixes that.
2013-01-01 00:49:11 -05:00
Tom Rothamel 44c73da13b Re-enable image cache. 2013-01-01 00:39:08 -05:00
Tom Rothamel 79daaf3387 Install a stub keymap as part of error handling.
This fixes a regression in parse error handling.
2012-12-31 22:41:45 -05:00
Tom Rothamel 6ccb9bc261 Prevent prediction from starving redraw/timer events.
The problem we had was that the REDRAW and TIMEEVENT events weren't
scheduled until the prediction coroutine was run. When prediction was
taking a while, the events wouldn't cancel it.

This manifiested as slow skipping, among other problems.
2012-12-29 22:52:30 -05:00
Tom Rothamel 5f6608e43a Have proper nosave for screens.
Previously, the nosave was typoed. This over-saved the screens,
and lead to very large save files.
2012-12-29 22:51:03 -05:00
Tom Rothamel dbdf3f1b61 "Fix" save dump.
Once in a while, we can get a weirdly reduced object. This ignores
those objects.
2012-12-29 22:50:19 -05:00
Tom Rothamel 39bc31ede3 Fix skipping keymap. 2012-12-29 22:48:32 -05:00
Tom Rothamel 07eaf04454 Minor fixes to console. 2012-12-29 22:46:52 -05:00
Tom Rothamel c77be46006 Adjust help and look of input line. 2012-12-29 12:37:15 -05:00
Tom Rothamel 5be1d2dee8 Add recall support to the console. 2012-12-29 12:28:11 -05:00
Tom Rothamel b04b095055 Console changes:
* Add commands.
* Eliminate debug prefix from names.
* Remove now-obsolete code.
2012-12-29 10:20:41 -05:00
Tom Rothamel 0a26b7bdff Implement various console commands. 2012-12-29 00:48:56 -05:00
Tom Rothamel 2755ac54db Console runs Ren'Py and Python code. 2012-12-28 23:00:54 -05:00
Tom Rothamel e7155dae52 Add exception handling. 2012-12-28 01:06:42 -05:00
Tom Rothamel b499c5b7bc More fixes, such as forgetting the label once we've called it. 2012-12-28 00:11:47 -05:00
Tom Rothamel 30e06d21b7 Fix rollback/context bug. 2012-12-27 23:55:50 -05:00
Tom Rothamel c01b910aa5 Run Ren'Py code with some error handling. 2012-12-27 23:42:25 -05:00
xRenx 748920b136 Update sphinx/source/translations.rst
Just corrected a couple of typos (one was in some sort of hash, edited 'm' out).
2012-12-27 18:42:56 +00:00
Tom Rothamel e07fbd55cb Assign serial numbers consectuively between files.
This lets us be sure that we never create the same name twice in
a single process of Ren'Py, even if load_string is called in
quick succession.
2012-12-24 01:39:47 -05:00
Tom Rothamel 6cf01c4261 Add the ability to load a string into Ren'Py as a script file. 2012-12-24 01:37:44 -05:00
Tom Rothamel afdb276473 Switch how we run the debug console.
The console now runs in the main context, at least when it's
started in the main context.
2012-12-23 23:26:33 -05:00
Tom Rothamel 445d16bde6 Add non-greedy rollback.
Greedy rollback rolls back as far as it can without crossing a
checkpoint. Non-greedy rollback rolls back to the start of
the checkpoint statement, or the start of the current
statement if we're in a statement.

(This is used by the console to get back to the start of the
current statement.)
2012-12-23 23:23:11 -05:00
Tom Rothamel 3d9f73500c Back out rollback option due to problems.
The problem is that when we have two rollbackable contexts sharing
a store, variable changes in the inner context aren't rolled back
when a rollback occurs in the main context.
2012-12-23 21:46:41 -05:00
Tom Rothamel 165e3490fc Work on console.
This version converts the console from UI functions to screens,
and adds support for rolling back lines of input.
2012-12-22 23:59:22 -05:00
Tom Rothamel 7ab1a9b6d8 Call_in_new_context takes an _rollback argument.
This determines if rollback is allowed in the new context.
2012-12-22 23:56:49 -05:00
Tom Rothamel 526e44dbae Remove unnecessary loop from main, which prevented the game
from ending with a return.
2012-12-22 17:30:36 -05:00
Tom Rothamel f33f5fad35 Persist viewport range across a viewport replacemet cycle.
This prevents viewports from resetting to 0 when replaced.
2012-12-22 14:40:29 -05:00
Tom Rothamel ee36c66333 Encode the translation properly when hashing it. 2012-12-19 15:32:37 -05:00
Tom Rothamel fc6c7e7549 Add back config.game_menu_action. 2012-12-19 15:32:25 -05:00
Tom Rothamel eb4b3613cd Check in new debug console.
Thanks: Shiz, C, and delta.
2012-12-17 23:57:29 -05:00
Tom Rothamel 2859390ff7 Check in file deletions and changes. 2012-12-17 23:51:00 -05:00
Tom Rothamel 654c648feb Reorganize the unweildy 00screens.rpy file into 9 new files. 2012-12-17 23:49:15 -05:00
Tom Rothamel ad07fa19d7 Finish breaking 00library.rpy out into multiple parts. 2012-12-17 23:03:46 -05:00
Tom Rothamel 4d1cb10fd3 Move default config.keymap into 00keymap.rpy. 2012-12-17 09:57:30 -05:00
Tom Rothamel d936b0f482 Start refactoring common:
* Reassigned the init priorities.
* Started splitting 00library.rpy into multiple files.
2012-12-17 00:38:47 -05:00
Tom Rothamel 915b67758a Move the common directory to renpy/common. 2012-12-16 21:51:38 -05:00
Tom Rothamel 16b152ddf8 Document replay mode. 2012-12-16 21:32:22 -05:00
Tom Rothamel 3cc56f5c9d Don't translate 'a' and 'q' - it doesn't work. 2012-12-16 18:25:33 -05:00
Tom Rothamel ac69e0e45b Rename memory->replay, so that there's no confusion with functions that
manage computer memory.
2012-12-16 18:24:15 -05:00
Tom Rothamel e0f53d1030 More work on memories.
Implemented the EndMemory exception, EndMemory action, and
renpy.end_memory, which together end the current memory if we're in
memory mode.
2012-12-15 14:36:12 -05:00
Tom Rothamel 4e8eafe92e Memory Support and rollback changes/fixes.
This adds initial support for the memory mode, where we replay
a scene in a new context.

This requires a bunch of changes to rollback, so that it is possible
to rollback a context other than context[0]. Most notably,
RestartException has been replaced by RestartContext and
RestartTopContext.

This also fixes a bug where the rollback_limit was being decremented
even when a checkpoint wasn't reach. This manifiested as a reduced
amount of rollback, especially at the start of the game.
2012-12-15 14:10:36 -05:00
Tom Rothamel 263fd588af Allow run.sh to be scripted. 2012-12-15 14:10:22 -05:00
Tom Rothamel fac0b5af94 Add NoRollback to the default store. 2012-12-09 22:05:18 -05:00
Tom Rothamel 02a266f2d7 Add the ability to back up and restore the entire store. 2012-12-09 13:41:00 -05:00
Tom Rothamel 392df791ee Make the main_menu variable available even when we're not in the menu
context.
2012-12-08 15:30:35 -05:00
Tom Rothamel fa797518c1 Document translations. 2012-12-08 14:51:36 -05:00
Tom Rothamel 1791824b14 Add menu/string translations. 2012-12-08 11:16:02 -05:00
Tom Rothamel 407f37dd9e Add translation documentation. 2012-12-08 10:55:54 -05:00
Tom Rothamel af50b50110 Let the creator configure the name of the translation directory.
"Let the bikeshed-painting begin!"
2012-12-04 22:46:28 -05:00
Tom Rothamel 1af7616961 Actually check in the launcher translation support. 2012-12-04 22:46:14 -05:00
Tom Rothamel 0d8100c1d8 Add translation support to the launcher.
There's two parts to this. First off, there's a new "Generate
Translations" option, which lets the creator or translator generate
the translation files. Then, I added the ability to change the language
the launcher itself uses.
2012-12-04 22:33:48 -05:00
Tom Rothamel 9b8ab5430c Mark various statements as translatable. 2012-12-03 00:13:42 -05:00
Tom Rothamel d7e325ba89 The loader will look for translation-specific filenames
when loading images, music, etc.
2012-12-03 00:13:42 -05:00
Tom Rothamel cae94fe7eb The imagemap cache should take the current language into account, to
ensure that if the image is translated, the cache will be updated or
ignored.
2012-12-03 00:13:42 -05:00
Tom Rothamel bbb50748e4 Fix translate python statement. 2012-12-03 00:13:42 -05:00
Tom Rothamel c34253c6af Stop translate on exception; prevent double translation; make prefix/suffix translatable. 2012-12-03 00:13:42 -05:00
Tom Rothamel a027dd6579 Change the language option so it triggers the new translate
mechanism.
2012-12-03 00:13:42 -05:00
Tom Rothamel 4a5cdd31f4 Add menu choices to the list of strings to be translated. 2012-12-03 00:13:42 -05:00
Tom Rothamel 83703f757f Add a rot13 filter for translations. 2012-12-03 00:13:42 -05:00
Tom Rothamel 58f68de94a Translations can be activated. 2012-12-03 00:13:42 -05:00
Tom Rothamel 963dfd0c62 Scanning scripts to generate string translations. 2012-12-03 00:13:42 -05:00
Tom Rothamel cd7f00b9e3 Fix argument parsing.
Previously, the project and command were optional, which led to problems
when a required command argument was missing.
2012-12-03 00:13:42 -05:00
Tom Rothamel 0c0b560321 Implement string_scan function.
This scans a file for translatable strings.
2012-12-03 00:13:42 -05:00
Tom Rothamel 3c31888f54 Add the !t conversion type.
This type allows us to write [value!t], to get a translation of
value.
2012-12-03 00:13:41 -05:00
Tom Rothamel 1f057d8945 String translation support.
This removes the old .rpt support, and replaces it with a translate
string statement. The RENPY_UPDATE_STRINGS statement now writes
a .rpy file containing the new strings.
2012-12-03 00:13:41 -05:00
Tom Rothamel a28e207511 Write out translates. 2012-12-03 00:13:41 -05:00
Tom Rothamel 35dd42a9f9 Infrastructure to write translation files. 2012-12-03 00:13:41 -05:00
Tom Rothamel 9cb00851aa Encode say strings properly. 2012-12-03 00:13:41 -05:00
Tom Rothamel 73f4ae8d28 We can now translate blocks of text. 2012-12-03 00:13:41 -05:00
Tom Rothamel 5e0edd9d95 Chain translation blocks together; add a new EndTranslate block. 2012-12-03 00:13:41 -05:00
Tom Rothamel dfbb5aa79e Parser changes.
* Add translate statement.
* Add nointeract clause to say statement.
2012-12-03 00:13:41 -05:00
Tom Rothamel 62d9bbf522 Initial support for automatic addition of translate nodes.
This adds a new pass to script loading, one where Ren'Py iterates
through the script and groups say statements and translatable user
statements into translate blocks.
2012-12-03 00:13:41 -05:00
Tom Rothamel e832d56310 Start implementation of translation. 2012-12-03 00:13:41 -05:00
Tom Rothamel 45fc6fb1b2 Merge branch 'devel' of github.com:renpy/renpy into devel 2012-12-03 00:12:39 -05:00
Koichi Akabe be39d4c825 Merge with trunk 2012-10-31 22:53:09 +09:00
Tom Rothamel 7baf6559ed When doing error handling, we only build the styles needed for
error handling.

This prevents consistency error in styles (for example, a style inheriting from an undefined style) from causing a crash.
2012-10-29 19:47:41 -04:00
Tom Rothamel 13ee3550f9 Fix LiveTile range() problems.
LiveTile could crash with an error if the child render size or
displayable size were not integers.
2012-10-29 18:38:56 -04:00
Tom Rothamel 4f63b6f2e0 Hide shouldn't stop prediction. 2012-10-17 22:09:40 -04:00
Tom Rothamel 536d765359 Add the voice_tag parameter to Character, and config.voice_tag_callback.
To support the new voice tag feature, character gains a voice_tag
parameter, and the new config.voice_tag_callback variable comes into
existence. When a character speaks, config.voice_tag_callback is
called with that character's voice tag.
2012-09-17 19:50:51 -04:00
Albert Westra 20c4dcac26 Fix a couple ID10T Errors 2012-09-17 09:20:31 -07:00
Albert Westra 58bd1a7bde Added elif to a if line 2012-09-16 22:07:25 -07:00
Albert Westra bacb4f067d Added Comments in 00screen.rpy;
Added :doc: comment in chracter;
and Added voice_tag requirement in 00voice.rpy.
2012-09-16 22:01:43 -07:00
Albert Westra e4d82fcfc2 Add README to state what I am doing 2012-09-12 22:36:30 -07:00
Tom Rothamel 754f457574 Merge branch 'feature-font-group' 2012-09-12 23:11:28 -04:00
Tom Rothamel 96984da285 Remove use of renpy.game.args.warp.
This is the last use of a command-line argument in code that isn't
part of the run command. We can now start a Ren'Py game with a
command other than the run command.
2012-09-12 22:53:56 -04:00
Tom Rothamel 1d1d1cb405 Merge branch 'master' into devel 2012-09-11 22:55:45 -04:00
Tom Rothamel 6beb2ed831 Make Language() use deferred rollback. 2012-09-11 22:54:16 -04:00
Tom Rothamel 80ac28bd07 Implement deferred rollback.
This allows a rollback to begin in a menu, but not take effect until the
user returns to the top-level context.
2012-09-11 22:52:56 -04:00
Tom Rothamel e4e39cdfeb Ensure rollback respects rollback_limit. 2012-09-11 22:52:08 -04:00
Tom Rothamel 25c03ae410 When rolling back, roll back to the the rollback immediately after
the previous checkpoint.

This allows all the code that makes up the current screen to be re-run
when a rollback occurs.
2012-09-11 19:02:13 -04:00
Tom Rothamel 7ef4449fc1 Merge branch 'feature-font-group' into devel 2012-09-11 01:29:11 -04:00
Tom Rothamel 2c728bc9a4 Bump version. 2012-09-11 01:28:40 -04:00
Tom Rothamel b1353fdfcc Document font groups. 2012-09-11 01:13:32 -04:00
Tom Rothamel 3a3cad9fd4 Integrate FontGroup with subsegment.
This gets the FontGroup code working.
2012-09-11 00:24:30 -04:00
Tom Rothamel a573008ef1 Ignore attempts to play None as sound/music.
This is a minor behavior change. Before this, renpy.play would stop
the playing sound if called with None - which is the default in a lot
of circumstances.
2012-09-09 22:47:23 -04:00
Tom Rothamel 6e6784a5e7 Implement FontGroup.
This lets us register a group of fonts, with each covering a portion
of the unicode character space. A string can then be segmented based
on this, dividing it up into runs of characters using various fonts.
2012-09-07 00:21:06 -04:00
Tom Rothamel cf22336e3a Add subsegment method to TextSegment.
This gives a textsegment the ability to decide to break itself up
further based on the contents of the text supplied to it.
2012-09-06 23:11:45 -04:00
Tom Rothamel 72a8e029ad Fix RAPT docs. 2012-09-04 22:29:38 -04:00
Tom Rothamel 4142473dc0 A few more fixes to the Android packaging docs. 2012-08-28 20:16:14 -04:00
Tom Rothamel 6e2bb81a1c Merge remote-tracking branch 'remotes/ren/patch-3' 2012-08-28 19:42:32 -04:00
Tom Rothamel dd4fc916ec Remove unnecessary use of AVFormatParameters in dead code. 2012-08-28 19:13:25 -04:00
xRenx 8e755eb157 Update sphinx/source/android-packaging.rst
Changed some wordings, expanded on some points, added screenshots and a Troubleshooting section.
2012-08-27 19:28:21 +02:00
Koichi Akabe be69bd95af Use underline_position for vertical sideline calculation
This change is optimized for full width characters
2012-08-24 16:50:42 +09:00
Koichi Akabe bc19194c58 Use bbox.yMax instead of horiBearingY for simulation
If the height of the glyph is small, horiBearingY returns
a non-expected value for vertical layout simulation, but bbox.yMax
returns the same value for all glyphs.
2012-08-20 17:18:35 +09:00
Koichi Akabe 62ac5b09b1 Get GSUB table regardless of FT_HAS_VERTICAL 2012-08-19 21:04:06 +09:00
Koichi Akabe 9ccc66da0e Fix position calcuration on vertical layout simulaton 2012-08-19 15:33:54 +09:00
Koichi Akabe 1d6ec0fcfd Simulate vertical layout if the font doesn't support it 2012-08-19 13:25:40 +09:00
Koichi Akabe d9510a7569 Simulate vertical layout if the font doesn't support it 2012-08-19 13:24:51 +09:00
Koichi Akabe 5412165427 Add vertical style
* Add vert and horiz text tag
 * Add ttgsubtable to get vertical glyphs
 * Inherit vertical style on ruby and hyperlink
2012-08-19 10:17:37 +09:00
538 changed files with 18801 additions and 28157 deletions
+7 -7
View File
@@ -12,6 +12,9 @@ tmp
cache
log.txt
errors.txt
traceback.txt
styles.txt
/build
/dist
@@ -20,20 +23,15 @@ log.txt
/jedit
/lint.txt
/renpy.code
/traceback.txt
/testing*
/screenshot*
/errors.txt
/styles.txt
/Microsoft.VC90.CRT.manifest
/console.exe
/msvcr90.dll
/python27.dll
/renpy.exe
/lib/windows-x86
/lib/linux-x86_64
/lib/linux-i686
/lib/python2.7
/lib
/lib.old
/doc
/.pydevproject
/.pydevproject.bak
@@ -49,6 +47,8 @@ log.txt
/module/build
/module/gen
/editra
/launcher/game/theme
dl
renpy/vc_version.py
.externalToolBuilders
-4796
View File
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
==============================
The Ren'Py Visual Novel Engine
==============================
http://www.renpy.org
Branches
========
Ren'Py development takes place on two branches, ``master`` and
``devel``, as well as the occasional feature branch.
Master
-----
The master branch contains code that can be run using the libraries in
the latest (release or pre-release) version of Ren'Py. It's used for
bugfixes and features that do not require C or Cython code to
implement.
After checking out master, run::
./after_checkout.sh <path-to-renpy>
to link in the libraries from the most recent Ren'Py. (On Windows, you
will need to run this under msys or cygwin.)
Ren'Py can then be run by running renpy.exe, renpy.sh, or renpy.app as
appropriate.
Devel
-----
The devel branch contains code that requires a recompile of modules
implemented in C or Cython. Building devel requires you to have the
prerequisite libraries installed. Then set RENPY_DEPS_INSTALLED to
a \::-separated list of paths containing dependencies. For example::
export RENPY_DEPS_INSTALL=/usr::/usr/lib/x86_64-linux-gnu/
Finally, change into the modules directory, and run::
python setup.py install
Ren'Py can then be run by using python to run renpy.py.
Contributing
============
For bug fixes, documentation improvements, and simple changes, just
make a pull request. For more complex changes, it might make sense
to file an issue so we can discuss the design.
Executable
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python
import argparse
import os
import subprocess
from renpy import version_tuple #@UnresolvedImport
version = ".".join(str(i) for i in version_tuple)
short_version = ".".join(str(i) for i in version_tuple[:3])
print "Version", version
ap = argparse.ArgumentParser()
ap.add_argument("--release", action="store_true")
ap.add_argument("--prerelease", action="store_true")
ap.add_argument("--experimental", action="store_true")
ap.add_argument("--no-tag", "-n", action="store_true")
args = ap.parse_args()
if args.release:
links = [ "release", "prerelease", "experimental" ]
tag = True
elif args.prerelease:
links = [ "prerelease", "experimental" ]
tag = True
elif args.experimental:
links = [ "experimental" ]
tag = False
else:
links = [ ]
tag = False
os.chdir("/home/tom/ab/renpy/dl")
for i in links:
if os.path.exists(i):
os.unlink(i)
os.symlink(short_version, i)
os.chdir("/home/tom/ab/renpy")
if tag and not args.no_tag:
cmd = [ "git", "tag", "-a", version, "-m", "Ren'Py " + version ]
subprocess.check_call(cmd)
os.chdir("/home/tom/ab/website")
subprocess.check_call("./upload.sh")
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
cd ~/ab/website
./upload.sh
+13 -5
View File
@@ -1,8 +1,16 @@
#!/bin/bash
ln -s ../help.html tutorial/README.html
ln -s ../help.html the_question/README.html
ln -s ../help.html template/README.html
ROOT="$(dirname $(readlink -f $0))"
ln -s sphinx/source/license.rst LICENSE.txt
ln -s sphinx/build/html doc
ln -s "$ROOT/help.html" "$ROOT/tutorial/README.html"
ln -s "$ROOT/help.html" "$ROOT/the_question/README.html"
ln -s "$ROOT/help.html" "$ROOT/template/README.html"
ln -s "$ROOT/sphinx/source/license.rst" "$ROOT/LICENSE.txt"
ln -s "$ROOT/sphinx/build/html" "$ROOT/doc"
if [ "$1" != "" ]; then
ln -s "$1/lib" "$ROOT/lib"
ln -s "$1/renpy.app" "$ROOT"
ln -s "$1/renpy.exe" "$ROOT"
fi
-111
View File
@@ -1,111 +0,0 @@
#!/usr/bin/env python
import modulefinder
import shutil
from distutils.core import setup
import py2exe #@UnresolvedImport @UnusedImport
import sys
import zipfile
import traceback
import os
# The pythonpath on my system.
sys.path.insert(0, 'c:\\msys\\1.0\\newbuild\\install\\bin')
sys.path.insert(0, 'c:\\msys\\1.0\\newbuild\\install\\python')
def move_from_dist(fn):
"""
Moves the file `fn` from the dist directory to the main
directory. (This means we don't need symlinks on windows.)
"""
if os.path.isdir(fn):
shutil.rmtree(fn)
elif os.path.exists(fn):
os.unlink(fn)
os.rename("dist/" + fn, fn)
import renpy
renpy.setup_modulefinder(modulefinder) #@UndefinedVariable
def main():
sys.argv[1:] = [ 'py2exe', '-a', '--dll-excludes', 'w9xpopen.exe', ]
setup(name="Ren'Py",
windows=[ dict(script="renpy.py",
dest_base="renpy",
icon_resources=[ (1, "newicon.ico") ],
),
],
console=[ dict(script="renpy.py", dest_base="console") ],
zipfile='lib/windows-x86/renpy.code',
options={ 'py2exe' : { 'excludes' : [ 'doctest',
'pygame.macosx',
'pygame.surfarray',
'pygame.mixer',
'pygame.mixer_music',
'_ssl',
'_hashlib',
'win32con',
'win32api',
'Numeric',
'os2emxpath',
'macpath',
'multiprocessing',
'_multiprocessing',
],
'optimize' : 2,
} },
)
zfold = zipfile.ZipFile("dist/lib/windows-x86/renpy.code")
zfnew = zipfile.ZipFile("dist/lib/windows-x86/renpy.code.new", "w", zipfile.ZIP_STORED)
seen = { }
for fn in zfold.namelist():
if fn.startswith("renpy/"):
# Keep around .pyo files that load pyd files.
pydfn = fn.replace("/", ".").replace(".pyo", ".pyd")
if not os.path.exists("dist/lib/windows-x86/" + pydfn):
continue
if fn in seen:
continue
if fn == "SDL_mixer.dll":
continue
seen[fn] = True
zfnew.writestr(fn, zfold.read(fn))
zfold.close()
zfnew.close()
os.unlink("dist/lib/windows-x86/renpy.code")
os.rename("dist/lib/windows-x86/renpy.code.new", "dist/lib/windows-x86/renpy.code")
# Take these files from the old python.
shutil.copy("c:/Python26/Microsoft.VC90.CRT.manifest", "Microsoft.VC90.CRT.manifest")
shutil.copy("c:/Python26/msvcr90.dll", "msvcr90.dll")
move_from_dist("lib/windows-x86")
move_from_dist("console.exe")
move_from_dist("python27.dll")
move_from_dist("renpy.exe")
try:
main()
except:
traceback.print_exc()
-79
View File
@@ -1,79 +0,0 @@
#!/usr/bin/env python
import collections
import glob
import re
import os
# A map from string to filename, line number where the string is found.
strings = collections.defaultdict(list)
def process(fn):
lineno = 0
for data in file(fn, "r"):
lineno += 1
matches = [ ]
matches += re.finditer(r'\bu\"(\\"|[^"])+\"', data)
matches += re.finditer(r"\bu\'(\\'|[^'])+\'", data)
for m in matches:
s = m.group(0)[2:-1]
if "\\u" in s:
continue
if len(s) == 1:
continue
strings[s].append("%s:%d" % (fn, lineno))
if __name__ == "__main__":
files = [ ]
files += glob.glob("launcher/*.rpy")
files += glob.glob("common/*.rpy")
files += glob.glob("common/*.rpym")
files += glob.glob("common/_layout/*.rpym")
files += glob.glob("common/_compat/*.rpym")
if "launcher/strings.rpy" in files:
files.remove("launcher/strings.rpy")
for fn in files:
process(fn)
converse = [ ]
for k, v in strings.iteritems():
v = " ".join(sorted(v))
converse.append((v, k))
converse.sort()
f = file("launcher/strings.rpy", "w")
files = set(i for i in os.listdir("launcher") if i.endswith(".rpy"))
print >>f, """\
init python:
LAUNCHER_RPYS = %r
TRANSLATION_STRINGS = [
""" % files
for v, k in converse:
# print
# print "#:", v
# print "#, python-format"
print >>f, " u\"%s\"," % unicode(k)
print >>f, """\
]
"""
-914
View File
@@ -1,914 +0,0 @@
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
# See LICENSE.txt for license details.
# This file consists of renpy functions that aren't expected to be
# touched by the user too much. We reserve the _ prefix for names
# defined in the library.
init -1180 python:
# These are settings that the user can tweak to control the
# look of the main menu and the load/save/escape screens.
# basics: The version of Ren'Py this script is intended for, or
# None if it's intended for the current version.
config.script_version = None
# The minimum version of the module we work with. Don't change
# this unless you know what you're doing.
config.module_version = 6007001
# Should we warn the user if the module is missing or has a bad
# version?
config.module_warning = False
# basics: A map from a string that's displayed by the interface to
# a translated value of that string.
config.translations = { }
# Used internally to maintain compatiblity with old
# translations of strings.
config.old_names = { }
# basics: True if the skip indicator should be shown.
config.skip_indicator = True
# basics: The width of a thumbnail.
config.thumbnail_width = 66
# basics: The height of a thumbnail.
config.thumbnail_height = 50
# basics: If not None, the default value of the fullscreen
# preference when the game is first run.
config.default_fullscreen = None
# basics: If not None, the default value of the text_cps
# preference when the game is first run.
config.default_text_cps = None
# If not None, the default value of afm_time
config.default_afm_time = None
# If not None, the default value of afm_enable
config.default_afm_enable = None
# Should we automatically define images?
config.automatic_images = None
# Prefixes to strip from automatic images.
config.automatic_images_strip = [ ]
# A save to automatically load, if it exists.
config.auto_load = None
# Layers to clear when entering the menus.
config.menu_clear_layers = [ ]
# Should we start the game with scene black or just scene?
config.start_scene_black = False
# Voice and Sound samples.
config.sample_sound = None
config.sample_voice = None
# This is updated to give the user an idea of where a save is
# taking place.
save_name = ''
# Should the window be shown during transitions?
_window_during_transitions = False
def _default_with_callback(trans, paired=None):
if (_window_during_transitions and not
renpy.context_nesting_level() and
not renpy.count_displayables_in_layer('transient')):
# narrator("", interact=False)
ui.window(style=style.say_window["empty"])
ui.null()
return trans
config.with_callback = _default_with_callback
def _default_empty_window():
who = _last_say_who
if who is not None:
who = eval(who)
if who is None:
who = narrator
if isinstance(who, NVLCharacter):
nvl_show_core()
else:
store.narrator("", interact=False)
config.empty_window = _default_empty_window
style.skip_indicator = Style(style.default, heavy=True, help='The skip indicator.')
style.skip_indicator.xpos = 10
style.skip_indicator.ypos = 10
# This is used to jump to a label with a transition.
def _intra_jumps_core(label, transition):
renpy.transition(getattr(config, transition))
renpy.jump(label)
_intra_jumps = renpy.curry(_intra_jumps_core)
# The function that's used to translate strings in the game menu.
def _(s):
"""
Translates s into another language or something.
"""
if s in config.translations:
return config.translations[s]
if s in config.old_names and config.old_names[s] in config.translations:
return config.translations[config.old_names[s]]
return s
config.extend_interjection = "{fast}"
def extend(what, interact=True):
who = _last_say_who
if who is not None:
who = eval(who)
if who is None:
who = narrator
if isinstance(who, basestring):
who = unknown.copy(who)
# This ensures extend works even with NVL mode.
who.do_extend()
what = _last_say_what + config.extend_interjection + what
renpy.exports.say(who, what, interact=interact)
store._last_say_what = what
extend.record_say = False
# Are the windows currently hidden?
_windows_hidden = False
def toggle_skipping():
if not config.skipping:
config.skipping = "slow"
else:
config.skipping = None
if renpy.context()._menu:
renpy.jump("_noisy_return")
else:
renpy.restart_interaction()
config.help = None
def _help(help=None):
if help is None:
help = config.help
if help is None:
return
if renpy.has_label(help):
renpy.call_in_new_context(help)
return
_preferences.fullscreen = False
try:
import webbrowser
webbrowser.open_new("file:///" + config.basedir + "/" + help)
except:
pass
# Called to make a screenshot happen.
def _screenshot():
import os.path
import os
import __main__
# Pick the directory to save into.
dest = config.renpy_base.rstrip("/")
# Guess if we're an OSX App.
if dest.endswith("/Contents/Resources/autorun"):
# Go up 4 directories.
dest = os.path.dirname(dest)
dest = os.path.dirname(dest)
dest = os.path.dirname(dest)
dest = os.path.dirname(dest)
# Try to pick a filename.
i = 1
while True:
fn = os.path.join(dest, config.screenshot_pattern % i)
if not os.path.exists(fn):
break
i += 1
try:
renpy.screenshot(fn)
except:
import traceback
traceback.print_exc()
if config.screenshot_callback is not None:
config.screenshot_callback(fn)
def _screenshot_callback(fn):
renpy.notify(_("Saved screenshot as %s.") % fn)
config.screenshot_callback = _screenshot_callback
_predict_screens = [ ]
init -1180 python hide:
import os
config.screenshot_pattern = os.environ.get("RENPY_SCREENSHOT_PATTERN", "screenshot%04d.png")
def dump_styles():
if config.developer:
renpy.style.write_text("styles.txt")
# This is run when entering the game menu.
config.game_menu_action = None
def invoke_game_menu():
if renpy.context()._menu:
if renpy.context()._main_menu:
return
else:
renpy.jump("_noisy_return")
else:
if config.game_menu_action:
renpy.display.behavior.run(config.game_menu_action)
else:
renpy.call_in_new_context('_game_menu')
def keymap_toggle_skipping():
if renpy.context()._menu:
return
toggle_skipping()
def fast_skip():
if config.fast_skipping or config.developer:
config.skipping = "fast"
if renpy.context()._menu:
renpy.jump("_noisy_return")
def reload_game():
if not config.developer:
return
renpy.call_in_new_context("_save_reload_game")
def launch_editor():
if not config.developer:
return
filename, line = renpy.get_filename_line()
renpy.launch_editor([ filename ], line)
# The default keymap. We might also want to put some of this into
# the launcher.
km = renpy.Keymap(
rollback = renpy.rollback,
screenshot = _screenshot,
toggle_fullscreen = renpy.toggle_fullscreen,
toggle_music = renpy.toggle_music,
toggle_skip = keymap_toggle_skipping,
fast_skip = fast_skip,
game_menu = invoke_game_menu,
hide_windows = renpy.curried_call_in_new_context("_hide_windows"),
launch_editor = launch_editor,
dump_styles = dump_styles,
reload_game = reload_game,
developer = renpy.curried_call_in_new_context("_developer"),
quit = renpy.quit_event,
iconify = renpy.iconify,
help = _help,
choose_renderer = renpy.curried_call_in_new_context("_choose_renderer"),
)
config.underlay = [ km ]
def skip_indicator():
### skip_indicator default
# (text) The style and placement of the skip indicator.
if config.skip_indicator is True:
if config.skipping == "slow" and config.skip_indicator:
ui.text(_(u"Skip Mode"), style='skip_indicator')
if config.skipping == "fast" and config.skip_indicator:
ui.text(_(u"Fast Skip Mode"), style='skip_indicator')
return
if not config.skip_indicator:
return
if not config.skipping:
return
ui.add(renpy.easy.displayable(config.skip_indicator))
config.overlay_functions.append(skip_indicator)
# Hyperlink functions. Duplicated in _errorhandling.rpym.
def hyperlink_styler(target):
return style.hyperlink_text
def hyperlink_function(target):
if target.startswith("http:"):
try:
import webbrowser
webbrowser.open(target)
except:
pass
else:
renpy.call_in_new_context(target)
style.default.hyperlink_functions = (hyperlink_styler, hyperlink_function, None)
# Prediction of screens.
def predict():
for s in _predict_screens:
if renpy.has_screen(s):
renpy.predict_screen(s)
s = _game_menu_screen
if s is None:
return
if renpy.has_screen(s):
renpy.predict_screen(s)
return
if s.endswith("_screen"):
s = s[:-7]
if renpy.has_screen(s):
renpy.predict_screen(s)
return
config.predict_callbacks.append(predict)
def imagemap_auto_function(auto_param, variant):
rv = auto_param % variant
if renpy.loadable(rv):
return rv
else:
return None
config.imagemap_auto_function = imagemap_auto_function
label _hide_windows:
if renpy.context()._menu:
return
if _windows_hidden:
return
python:
_windows_hidden = True
ui.saybehavior(dismiss=['dismiss', 'hide_windows'])
ui.interact(suppress_overlay=True, suppress_window=True)
_windows_hidden = False
return
# This code here handles check for the correct version of the Ren'Py module.
label _save_reload_game:
python hide:
renpy.take_screenshot((config.thumbnail_width, config.thumbnail_height))
renpy.music.stop()
ui.add(Solid((0, 0, 0, 255)))
ui.text("Saving game...",
size=32, xalign=0.5, yalign=0.5, color=(255, 255, 255, 255))
renpy.pause(0)
renpy.save("_reload-1", "reload save game")
ui.add(Solid((0, 0, 0, 255)))
ui.text("Reloading script...",
size=32, xalign=0.5, yalign=0.5, color=(255, 255, 255, 255))
renpy.pause(0)
renpy.utter_restart()
label _load_reload_game:
if not renpy.can_load("_reload-1"):
return
python hide:
renpy.rename_save("_reload-1", "_reload-2")
ui.add(Solid((0, 0, 0, 255)))
ui.text("Reloading game...",
size=32, xalign=0.5, yalign=0.5, color=(255, 255, 255, 255))
ui.pausebehavior(0)
ui.interact(suppress_underlay=True, suppress_overlay=True)
renpy.load("_reload-2")
return
init -1001:
image text = renpy.ParameterizedText(style="centered_text")
# Lock the library object.
$ config.locked = True
# Implement config.default_fullscreen and config.default_text_cps.
init 1180 python:
if not persistent._set_preferences:
persistent._set_preferences = True
if config.default_fullscreen is not None:
_preferences.fullscreen = config.default_fullscreen
if config.default_text_cps is not None:
_preferences.text_cps = config.default_text_cps
if config.default_afm_time is not None:
_preferences.afm_time = config.default_afm_time
if config.default_afm_enable is not None:
_preferences.afm_enable = config.default_afm_enable
_preferences.using_afm_enable = True
else:
_preferences.afm_enable = True
_preferences.using_afm_enable = False
##############################################################################
# Code that originated in 00gamemenu.rpy
init -1180 python:
######################################################################
# First up, we define a bunch of configuration variable, which the
# user can change.
# menus: Music to play when entering the game menu.
config.game_menu_music = None
# menus: Sound played when entering the library without clicking a
# button.
config.enter_sound = None
# menus: Sound played when leaving the library without clicking a
# button.
config.exit_sound = None
# menus: Transition that occurs when entering the game menu.
config.enter_transition = None
# menus: Transition that occurs when leaving the game menu.
config.exit_transition = None
# menus: Transition that's used when going from one screen to another.
config.intra_transition = None
# menus: Transition that's used when going from the main to the game
# menu.
config.main_game_transition = None
# menus: Transition that's used when going from the game to the main
# menu.
config.game_main_transition = None
# menus: Transition that's used at the end of the game, when returning
# to the main menu.
config.end_game_transition = None
# menus: Transition that's used at the end of the splash screen, when
# it is shown.
config.end_splash_transition = None
# Transition that's used after the game is loaded.
config.after_load_transition = None
# basics: True if autosave should be used.
config.has_autosave = True
# basics: True if quicksave has been enabled.
config.has_quicksave = False
# A list of layers to clear when entering the main and game menus.
config.clear_layers = [ ]
# The _window_subtitle used inside menus.
config.menu_window_subtitle = ''
# The screen that we go to when entering the game menu.
_game_menu_screen = None
style.error_root = Style(style.default)
style.error_title = Style(style.default)
style.error_body = Style(style.default)
def _show_exception(title, message):
ui.window(style='error_root')
ui.vbox()
ui.text(title, style='error_title')
ui.text("")
ui.text(message, style='error_body')
ui.text("")
ui.text(_(u"Please click to continue."), style='error_body')
ui.close()
ui.saybehavior()
ui.interact()
def _enter_menu():
config.skipping = None
renpy.movie_stop(only_fullscreen=True)
renpy.take_screenshot((config.thumbnail_width, config.thumbnail_height))
for i in config.menu_clear_layers:
renpy.scene(layer=i)
renpy.context()._menu = True
renpy.context()._main_menu = False
renpy.context_dynamic("main_menu")
renpy.context_dynamic("_window_subtitle")
renpy.context_dynamic("_window")
store.main_menu = False
store._window_subtitle = config.menu_window_subtitle
store._window = False
store.mouse_visible = True
store.suppress_overlay = True
ui.clear()
for i in config.clear_layers:
renpy.scene(layer=i)
# Run at the end of init, to set up autosaving based on the user's
# choices.
init 1180 python:
if config.has_autosave:
config.autosave_slots = 10
else:
config.autosave_frequency = None
# Factored this all into one place, to make our lives a bit easier.
label _enter_game_menu:
$ _enter_menu()
$ renpy.transition(config.enter_transition)
if renpy.has_label("enter_game_menu"):
call expression "enter_game_menu" from _call_enter_game_menu_1
if config.game_menu_music:
$ renpy.music.play(config.game_menu_music, if_changed=True)
return
# Entry points from the game into menu-space.
label _game_menu(_game_menu_screen=_game_menu_screen):
if not _game_menu_screen:
return
$ renpy.play(config.enter_sound)
call _enter_game_menu from _call__enter_game_menu_0
if renpy.has_label("game_menu"):
jump expression "game_menu"
if renpy.has_screen(_game_menu_screen):
$ renpy.show_screen(_game_menu_screen)
$ ui.interact()
jump _noisy_return
jump expression _game_menu_screen
label _game_menu_save:
call _enter_game_menu from _call__enter_game_menu_1
if renpy.has_label("_save_screen"):
jump expression "_save_screen"
else:
jump expression "save_screen"
label _game_menu_load:
call _enter_game_menu from _call__enter_game_menu_2
if renpy.has_label("_load_screen"):
jump expression "_load_screen"
else:
jump expression "load_screen"
label _game_menu_preferences:
call _enter_game_menu from _call__enter_game_menu_3
if renpy.has_label("_prefs_screen"):
jump expression "_prefs_screen"
else:
jump expression "preferences_screen"
label _quit:
$ renpy.quit()
label _return_skipping:
$ config.skipping = "slow"
jump _return
# Make some noise, then return.
label _noisy_return:
$ renpy.play(config.exit_sound)
# Return to the game.
label _return:
if renpy.context()._main_menu:
$ renpy.transition(config.game_main_transition)
jump _main_menu_screen
$ renpy.transition(config.exit_transition)
return
label _confirm_quit:
if renpy.has_label("confirm_quit"):
jump expression "confirm_quit"
elif renpy.has_label("_compat_confirm_quit"):
jump expression "_compat_confirm_quit"
else:
jump expression "_quit_prompt"
##############################################################################
# Code that originated in 00mainmenu.rpy
init -1180 python hide:
# menus: Music to play at the main menu.
config.main_menu_music = None
# advanced: Callbacks to run at start.
config.start_callbacks = [ ]
# This fixes up the context, if necessary, then calls the real
# after_load.
label _after_load:
$ renpy.context()._menu = False
$ renpy.context()._main_menu = False
if config.after_load_transition:
$ renpy.transition(config.after_load_transition, force=True)
if renpy.has_label("after_load"):
jump expression "after_load"
else:
return
# This is the true starting point of the program. Sssh... Don't
# tell anyone.
label _start:
python hide:
renpy.context()._menu = False
renpy.context()._main_menu = False
for i in config.start_callbacks:
i()
$ renpy.block_rollback()
call _gl_test
call _load_reload_game from _call__load_reload_game_1
if not _restart and config.auto_load and renpy.can_load(config.auto_load):
$ renpy.load(config.auto_load)
if config.start_scene_black:
scene black
else:
scene
if not _restart:
$ ui.pausebehavior(0)
$ ui.interact(suppress_underlay=True, suppress_overlay=True)
$ renpy.block_rollback()
$ _old_game_menu_screen = _game_menu_screen
$ _old_predict_screens = _predict_screens
$ _game_menu_screen = None
$ _predict_screens = [ 'main_menu' ]
if renpy.has_label("splashscreen") and not _restart:
call expression "splashscreen" from _call_splashscreen_1
$ _game_menu_screen = _old_game_menu_screen
$ del _old_game_menu_screen
$ renpy.block_rollback()
if config.main_menu_music:
$ renpy.music.play(config.main_menu_music, if_changed=True)
else:
$ renpy.music.stop()
# Clean out any residual scene from the splashscreen.
if config.start_scene_black:
scene black
else:
scene
# This has to be python, to deal with a case where _restart may
# change across a shift-reload.
python:
if _restart is None:
renpy.transition(config.end_splash_transition)
else:
renpy.transition(_restart[0])
renpy.jump(_restart[1])
label _invoke_main_menu:
# Again, this has to be python.
python:
if _restart:
renpy.call_in_new_context(_restart[2])
else:
renpy.call_in_new_context("_main_menu")
# If the main menu returns, then start the game.
jump start
# At this point, we've been switched into a new context. So we
# initialize it.
label _main_menu(_main_menu_screen="_main_menu_screen"):
$ _enter_menu()
python:
renpy.dynamic("_load_prompt")
_load_prompt = False
renpy.context()._main_menu = True
store.main_menu = True
jump expression _main_menu_screen
# This is called to show the main menu to the user.
label _main_menu_screen:
# Let the user completely override the main menu. (But please note
# it still lives in the menu context, rather than the game context.)
if renpy.has_label("main_menu"):
jump expression "main_menu"
# New name.
elif renpy.has_label("main_menu_screen"):
jump expression "main_menu_screen"
# Compatibility name.
elif renpy.has_label("_library_main_menu"):
jump expression "_library_main_menu"
return
init 1180 python hide:
def create_automatic_images():
seps = config.automatic_images
if seps is True:
seps = [ ' ', '/', '_' ]
for dir, fn in renpy.loader.listdirfiles():
if fn.startswith("_"):
continue
# Only .png and .jpg
if not fn.lower().endswith(".png") and not fn.lower().endswith(".jpg"):
continue
# Strip the extension, replace slashes.
shortfn = fn[:-4].replace("\\", "/")
# Determine the name.
name = ( shortfn, )
for sep in seps:
name = tuple(j for i in name for j in i.split(sep))
# Strip name components.
while name:
for i in config.automatic_images_strip:
if name[0] == i:
name = name[1:]
break
else:
break
# Only names of 2 components or more.
if len(name) < 2:
continue
# Reject if it already exists.
if name in renpy.display.image.images:
continue
renpy.image(name, fn)
if config.automatic_images:
create_automatic_images()
# After init, make some changes based on if config.developer is True.
init 1180 python hide:
if config.developer:
if config.debug_sound is None:
config.debug_sound = True
renpy.load_module("_developer")
# Entry point for the developer screen. The rest of it is loaded from
# _developer.rpym
label _developer:
if not config.developer:
return
$ _enter_menu()
jump expression "_developer_screen"
# Translations.
init -1180 python:
def _language_activate():
if persistent._language:
language = persistent._language
else:
language = "translations"
if renpy.loadable(language + ".rpt"):
config.translator = renpy.Translator(language)
else:
config.translator = None
_language_activate()
-46
View File
@@ -1,46 +0,0 @@
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
# See LICENSE.txt for license details.
# Common stuff that's used by the various Ren'Py menus. This includes
# the default definitions of config.main_menu and config.game_menu, as
# well as the default definitions of the various preferences (and the objects
# holding those preferences)
init -1150 python:
config.main_menu = [
(u"Start Game", "start", "True"),
(u"Load Game", _intra_jumps("load_screen", "main_game_transition"), "True"),
(u"Preferences", _intra_jumps("preferences_screen", "main_game_transition"), "True"),
(u"Help", _help, "True", "config.help"),
(u"Quit", ui.jumps("_quit"), "True"),
]
config.game_menu = [
( None, u"Return", ui.jumps("_return"), 'True'),
( "preferences", u"Preferences", _intra_jumps("preferences_screen", "intra_transition"), 'True' ),
( "save", u"Save Game", _intra_jumps("save_screen", "intra_transition"), 'not main_menu' ),
( "load", u"Load Game", _intra_jumps("load_screen", "intra_transition"), 'True'),
( None, u"Main Menu", ui.callsinnewcontext("_main_menu_prompt"), 'not main_menu' ),
( "help", u"Help", _help, "True", "config.help"),
( None, u"Quit", ui.callsinnewcontext("_quit_prompt"), 'True' ),
]
label _quit_prompt:
$ renpy.loadsave.force_autosave()
if layout.yesno_prompt(None, layout.QUIT):
jump _quit
else:
return
label _main_menu_prompt:
$ renpy.loadsave.force_autosave()
if layout.yesno_prompt(None, layout.MAIN_MENU):
$ renpy.full_restart(transition=config.game_main_transition)
else:
return
-1946
View File
File diff suppressed because it is too large Load Diff
-131
View File
@@ -1,131 +0,0 @@
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
# See LICENSE.txt for license details.
# This extra contains a basic implementation of voice support. Right
# now, voice is given its own toggle, and can either be turned on or
# turned off. In the future, we'll probably provide some way of
# toggling it on or off for individual characters.
#
# To use it, place a voice "<sndfile>" line before each voiced line of
# dialogue.
#
# voice "e_1001.ogg"
# e "Voice support lets you add the spoken word to your games."
#
# Normally, a voice is cancelled at the start of the next
# interaction. If you want a voice to span interactions, call
# voice_sustain.
#
# voice "e_1002.ogg"
# e "Voice sustain is a technique that allows the same voice file.."
#
# $ voice_sustain()
# e "...to play for two lines of dialogue."
init -1120:
python:
_voice = object()
_voice.play = None
_voice.sustain = False
_voice.seen_in_lint = False
# Call this to specify the voice file that will be played for
# the user.
def voice(file, **kwargs):
if not config.has_voice:
return
_voice.play = file
_last_voice_play = file
# Call this to specify that the currently playing voice file
# should be sustained through the current interaction.
def voice_sustain(ignored="", **kwargs):
if not config.has_voice:
return
_voice.sustain = True
# Call this to replay the last bit of voice.
def voice_replay():
renpy.sound.play(_last_voice_play, channel="voice")
# Returns true if we can replay the voice.
def voice_can_replay():
return _last_voice_play != None
python hide:
# basics: True if the game will have voice.
config.has_voice = True
# This is called on each interaction, to ensure that the
# appropriate voice file is played for the user.
def voice_interact():
if not config.has_voice:
return
if _voice.play and not config.skipping:
renpy.sound.play(_voice.play, channel="voice")
store._last_voice_play = _voice.play
elif not _voice.sustain:
renpy.sound.stop(channel="voice")
store._last_voice_play = _voice.play
_voice.play = None
_voice.sustain = False
config.start_interact_callbacks.append(voice_interact)
config.say_sustain_callbacks.append(voice_sustain)
def voice_afm_callback():
return not renpy.sound.is_playing(channel="voice")
config.afm_callback = voice_afm_callback
python early hide:
def parse_voice(l):
fn = l.simple_expression()
if fn is None:
renpy.error('expected simple expression (string)')
if not l.eol():
renpy.error('expected end of line')
return fn
def execute_voice(fn):
fn = eval(fn)
voice(fn)
def lint_voice(fn):
_voice.seen_in_lint = True
fn = _try_eval(fn, 'voice filename')
if isinstance(fn, basestring) and not renpy.loadable(fn):
renpy.error('voice file %r is not loadable' % fn)
renpy.statements.register('voice',
parse=parse_voice,
execute=execute_voice,
lint=lint_voice)
def parse_voice_sustain(l):
if not l.eol():
renpy.error('expected end of line')
return None
def execute_voice_sustain(parsed):
voice_sustain()
renpy.statements.register('voice sustain',
parse=parse_voice_sustain,
execute=execute_voice_sustain)
-12
View File
@@ -1,12 +0,0 @@
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
# See LICENSE.txt for license details.
init python hide:
layout.provides('yesno_prompt')
def yesno_prompt(screen, message):
rv = renpy.call_screen('yesno_prompt', message=message, yes_action=Return(True), no_action=Return(False))
return rv
layout.yesno_prompt = yesno_prompt
+29 -10
View File
@@ -1,4 +1,4 @@
#!/home/tom/bin/renpython
#!/home/tom/bin/renpython -OO
# Builds a distributions of Ren'Py.
import sys
@@ -79,7 +79,31 @@ def zipup(filename, prefix, files):
sys.stdout.flush()
def copy_tutorial_file(src, dest):
"""
Copies a file from src to dst. Lines between "# tutorial-only" and
"# end-tutorial-only" comments are omitted from the copy.
"""
sf = open(src, "rb")
df = open(dest, "wb")
# True if we want to copy the line.
copy = True
for l in sf:
if "# tutorial-only" in l:
copy = False
elif "# end-tutorial-only" in l:
copy = True
else:
if copy:
df.write(l)
sf.close()
df.close()
def tree(root):
rv = [ ]
@@ -95,7 +119,6 @@ def tree(root):
if ".doctrees" in dirs:
dirs.remove(".doctrees")
for f in filenames:
if f[-1] == '~' or f[0] == '.':
continue
@@ -131,7 +154,7 @@ def main():
match_version = ".".join(str(i) for i in renpy.version_tuple[:2]) #@UndefinedVariable
zip_version = ".".join(str(i) for i in renpy.version_tuple[:3]) #@UndefinedVariable
s = subprocess.check_output([ "git", "describe", "--dirty", "--match", match_version ])
s = subprocess.check_output([ "git", "describe", "--tags", "--dirty", "--match", match_version ])
parts = s.strip().split("-")
vc_version = int(parts[1])
@@ -152,12 +175,8 @@ def main():
print "Version {} ({})".format(args.version, full_version)
# Copy over the screens, to keep them up to date.
shutil.copy("tutorial/game/screens.rpy", "template/game/screens.rpy")
# Copy in the windows version of zsync
shutil.copy("../zsync.exe", "lib/windows-x86/zsync.exe")
shutil.copy("../zsyncmake.exe", "lib/windows-x86/zsyncmake.exe")
copy_tutorial_file("tutorial/game/screens.rpy", "template/game/screens.rpy")
# Compile all the python files.
compileall.compile_dir("renpy/", ddir="renpy/", force=1, quiet=1)
@@ -215,7 +234,7 @@ def main():
if not args.fast:
shutil.copy("renpy-ppc.zip", os.path.join(destination, "renpy-ppc.zip"))
# shutil.copy("renpy-ppc.zip", os.path.join(destination, "renpy-ppc.zip"))
with open("7z.sfx", "rb") as f:
sfx = f.read()
-137
View File
@@ -1,137 +0,0 @@
# This contains code for the new day planner. You probably
# don't want to change this file, but it might make sense to
# change many of the variables or styles defined here from
# other files.
init -100 python:
# The frame containing the day planner.
style.dp_frame = Style(style.frame)
style.dp_vbox = Style(style.vbox)
style.dp_hbox = Style(style.hbox)
# The frame and vbox containing a single choice.
style.dp_choices = Style(style.default)
style.dp_choices_vbox = Style(style.vbox)
style.dp_choices.xalign = 0.5
# Buttons.
style.dp_choice_button = Style(style.button)
style.dp_choice_button_text = Style(style.button_text)
style.dp_done_button = Style(style.button)
style.dp_done_button_text = Style(style.button_text)
# Labels.
style.dp_label = Style(style.label)
style.dp_label_text = Style(style.label_text)
# The title of the done button.
dp_done_title = "Done Planning"
# A map from period name to the information we know about that
# period.
__periods = { }
# The period we're updating.
__period = None
class __Period(object):
def __init__(self, name, var):
self.name = name
self.var = var
self.acts = [ ]
def dp_period(name, var):
__periods[name] = store.__period = __Period(name, var)
__None = object()
def dp_choice(name, value=__None, enable="True", show="True"):
if not __period:
raise Exception("Choices must be part of a defined period.")
if value is __None:
value = name
__period.acts.append((name, value, enable, show))
def __set_noncurried(var, value):
setattr(store, var, value)
return True
__set = renpy.curry(__set_noncurried)
label day_planner(periods):
python hide:
renpy.choice_for_skipping()
label day_planner_repeat:
if renpy.has_label("dp_callback"):
call dp_callback
python hide:
ui.window(style=style.dp_frame)
ui.vbox(style=style.dp_vbox)
ui.hbox(style=style.dp_hbox)
can_continue = True
for p in periods:
if p not in __periods:
raise Exception("Period %r was never defined." % p)
ui.window(style=style.dp_choices)
ui.vbox(style=style.dp_choices_vbox)
p = __periods[p]
v = getattr(store, p.var)
layout.label(p.name, "dp")
valid_choice = False
for name, value, enable, show in p.acts:
show = eval(show)
enable = eval(enable)
selected = (v == value)
if show:
layout.button(
name,
"dp_choice",
clicked=__set(p.var, value),
selected=selected,
enabled=enable,
)
if show and enable and selected:
valid_choice = True
if not valid_choice:
can_continue = False
ui.close()
ui.close() # hbox.
layout.button(
dp_done_title,
"dp_done",
clicked=ui.returns(False),
enabled=can_continue)
ui.close() # vbox
if ui.interact():
jump day_planner_repeat
else:
return
-328
View File
@@ -1,328 +0,0 @@
# The Ren'Py/DSE event dispatcher. This file contains the code that
# actually supports the running of events. Specifically, it contains
# code that determines which events are available, which events can
# run, and to actually run the events that should be run during a
# given period.
# This isn't really intended to be user-changable.
init -100 python:
# A list of all of the events that the system knowns about,
# it's filtered to determine which events should run when.
all_events = [ ]
# The base class for events. When constructed, an event
# automatically adds itself to all_events.
#
# The first parameter for this is a unique name for the event,
# which is also used as the label that is called when the
# event executes.
#
# All other parameters are expressions. These expressions can
# be either strings, or objects having an eval method. Many
# interesting objects are given below.
#
# Keyword arguments are also kept on the object. Currently,
# there is one useful keyword argument, priority. This
# controls the order in which events are in the event list.
# (Events with lower priority number are evaluated first. If a
# priority is not specified, it's 100.)
class event(object):
def __repr__(self):
return '<event ' + self.name + '>'
def __init__(self, name, *args, **kwargs):
self.name = name
exprs = [ ]
for i in args:
if isinstance(i, basestring):
exprs.append(event.evaluate(i))
else:
exprs.append(i)
self.exprs = exprs
self.priority = kwargs.get('priority', 100)
all_events.append(self)
# Checks to see if this event is valid. It's called with
# a list of events that have already checked out to be
# True, and returns True if this event checks out.
def check(self, valid):
for i in self.exprs:
if not i.eval(self.name, valid):
return False
return True
def properties(self):
rv = { }
for i in self.exprs:
rv.update(i.properties())
return rv
# The base class for all of the event checks given below.
class event_check(object):
def properties(self):
return { }
def __invert__(self):
return event.false(self)
def __and__(self, other):
return event.and_op(self, other)
def __or__(self, other):
return event.or_op(self, other)
# This evaluates the expression given as an argument, and the
# returns true if it evaluates to true.
class evaluate(event_check):
def __init__(self, expr):
self.expr = expr
def eval(self, name, valid):
return eval(self.expr)
# If present as a condition to an event, an object of this
# type ensures that the event will only execute once.
class once(event_check):
def eval(self, name, valid):
return name not in events_executed
# Returns True if no event of higher priority can execute,
# and false otherwise. In general, solo events should be
# the lowest priority, and are run if nothing else is.
class solo(event_check):
def eval(self, name, valid):
# True if valid is empty.
return not valid
# Returns True if no event of higher priority can execute.
# This also prevents other events from executing.
class only(solo):
def properties(self):
return dict(only=True)
# Returns True if the given events have happend already,
# at any time in the game. False if at least one hasn't
# happened yet.
class happened(event_check):
def __init__(self, *events):
self.events = events
def eval(self, name, valid):
for i in self.events:
if i not in events_executed:
return False
return True
# Returns True probability of the time, where probability
# is a number between 0 and 1.
class random(event_check):
def __init__(self, probability):
self.probability = probability
def eval(self, name, valid):
return renpy.random.random() <= self.probability
# Chooses only one from the group.
class choose_one(event_check):
def __init__(self, group, group_count=1):
self.group = group
self.group_count = group_count
def eval(self, name, valid):
return True
def properties(self):
return dict(group=self.group,
group_count=self.group_count)
# Evaluates its argument, and returns true if it is false
# and vice-versa.
class false(event_check):
def __init__(self, cond):
self.cond = cond
def eval(self, name, valid):
return not self.cond.eval(name, valid)
# Handles the and operator.
class and_op(event_check):
def __init__(self, *args):
self.args = args
def eval(self, name, valid):
for i in self.args:
if not i.eval(name, valid):
return False
return True
# Handles the or operator.
class or_op(event_check):
def __init__(self, *args):
self.args = args
def eval(self, name, valid):
for i in self.args:
if i.eval(name, valid):
return True
return False
# Returns True if all of the events given as arguments have
# happened yesterday or earlier, or False otherwise.
class depends(event_check):
def __init__(self, *events):
self.events = events
def eval(self, name, valid):
for i in self.events:
if i not in events_executed_yesterday:
return False
return True
# The number of periods to skip.
skip_periods = 0
# This returns True if the current period should be skipped,
# or False if the current period should execute. If it returns
# True, it decrements skip_periods.
def check_skip_period():
global skip_periods
if skip_periods:
skip_periods -= 1
return True
else:
return False
def __events_init():
store.events_executed = { }
store.events_executed_yesterday = { }
config.start_callbacks.append(__events_init)
# This should called at the end of a (game) day, to let things
# like depends_yesterday to work.
label events_end_day:
$ skip_periods = 0
python hide:
# We can't skip between days.
skip_periods = 0
for k in events_executed:
events_executed_yesterday[k] = True
return
# This is called once per period, to determine, and then execute, the
# events that should be run for that period.
label events_run_period:
$ events = [ ]
python hide:
eobjs = [ ]
egroups = { }
eingroup = { }
for i in all_events:
if not i.check(eobjs):
continue
eobjs.append(i)
props = i.properties()
if 'group' in props:
group = props['group']
count = props['group_count']
egroups.setdefault(group, [ ]).extend([ i ] * count)
eingroup[i] = group
if 'only' in props:
break
echosen = { }
for k in egroups:
echosen[k] = renpy.random.choice(egroups[k])
for i in eobjs:
if i in eingroup and echosen[eingroup[i]] is not i:
continue
events.append(i.name)
while not check_skip_period() and events:
$ _event = events.pop(0)
$ events_executed[_event] = True
call expression _event from call_expression_event_1
return
# If this is jumped to, it will end the current period immediately,
# and return control to the main program.
label events_end_period:
$ skip_period = 1
return
# If this is jumped to, it will end the current period and skip
# the next period. Use this if, say, the user goes on a date that
# takes up two time slots.
label events_skip_period:
$ skip_period = 2
return
init 100:
python hide:
# Sort all events on priority.
all_events.sort(key=lambda i : i.priority)
python hide:
for i in all_events:
if not renpy.has_label(i.name):
raise Exception("'%s' is defined as an event somewhere in the game, but no label named '%s' was defined anywhere." % (i.name, i.name))
-767
View File
@@ -1,767 +0,0 @@
# This file contains the events that will be part of the game. It's
# expected that the user will add and remove events as appropriate
# for this game.
# Some characters that are used in events in the game.
init:
$ t = Character('Teacher')
$ gg = Character('Glasses Girl', color=(192, 255, 192, 255))
$ sg = Character('Sporty Girl', color=(255, 255, 192, 255))
$ bg = Character('Both Girls')
$ narrator = Character(' ')
# First up, we define some simple events for the various actions, that
# are run only if no higher-priority event is about to occur.
init:
$ event("class", "act == 'class'", event.only(), priority=200)
$ event("class_bad", "act == 'class'", priority=210)
$ event("cut1", "act == 'cut'", event.choose_one('cut'), priority=200)
$ event("cut2", "act == 'cut'", event.choose_one('cut'), priority=200)
$ event("study", "act == 'study'", event.solo(), priority=200)
$ event("hang", "act == 'hang'", event.solo(), priority=200)
$ event("exercise", "act == 'exercise'", event.solo(), priority=200)
$ event("play", "act == 'play'", event.solo(), priority=200)
label class:
"I make it to class just in time, and proceed to listen to the
teacher droning on about a wide range of topics, none of which
are remotely interesting."
return
# For test purposes only.
label class_bad:
"You shouldn't be seeing this."
"This is because class was declared with event.only(), which
should suspend processing of further events."
"This is really for testing purposes only."
return
label cut1:
"I cut class, and spend the morning goofing off instead."
$ intelligence -= 10
return
label cut2:
"I cut class, and spend the morning playing computer games."
return
label study:
"I head on down to the library, and start reading about the topics
I should have been reading about in class."
$ intelligence += 10
return
label hang:
"I spend the afternoon hanging out with my friends, killing
some time."
return
label exercise:
"I decide to go out for a run through the town, to keep myself in
shape."
$ strength += 10
return
label play:
"I pop a DVD into my video game console, and spend the evening
rolling small cities up into balls."
$ strength -= 10
return
# Below here are special events that are triggered when certain
# conditions are true.
# This is an introduction event, that runs once when we first go
# to class.
init:
$ event("introduction", "act == 'class'", event.once(), event.only())
label introduction:
"I run to school, and make it to my seat just as the bell
signalling the start of class rings."
t "Before we start, I have an announcement to make."
t "We will have two new students joining us. Girls, come on in."
"Two girls walk in, and stand in front of the class."
"They're twins."
"Identical twins."
"Identical black hair, and the same pretty face."
"Despite that, it's still fairly easy to tell them apart."
"The one on the left is wearing glasses."
"Not too thick, but enough to let me know she probably reads alot
of books."
"If I look a little closely, I can find another difference."
"The one on the right probably exercises a bit more."
"I can tell by the muscle tone in her legs."
"I realize that I'm staring at her legs, and quickly look up."
"Suddenly, I realize that she's been talking for all this town."
sg "... to this town. And we hope to be friends with all of you."
sg "Well, that's about it. Sis, do you have anything to say?"
"The girl with glasses pauses for a second, and then quickly says:"
gg "{size=-4}It's good to meet you all.{/size}"
"She stops, and goes back to not saying anything."
t "Well, if that's all, you can take your seats and we can start
the class."
"They do, and our teacher begins his lecture."
"I don't think anyone pays much attention to it, however."
return
# These are the events with glasses girl.
init:
# The glasses girl is studying in the library, but we do not
# talk to her.
#
#
$ event("gg_studying",
# This takes place when the action is 'study'.
"act == 'study'",
# This will only take place if no higher-priority
# event will occur.
event.solo(),
# This takes place at least one day after seeing the
# introduction event.
event.depends("introduction"),
# This takes priority over the study event.
priority=190)
# She asks to borrow our pen.
$ event("borrow_pen",
# This takes place when we go to study, and we have an int
# >= 50.
"act == 'study' and intelligence >= 50",
# It runs only once.
event.once(),
# It requires the introduction event to have run at least
# one day before.
event.depends("introduction"))
# After the pen, she smiles when she sees us.
$ event("gg_smiling", "act == 'study'",
event.solo(), event.depends("borrow_pen"),
priority = 180)
# The bookslide.
$ event("bookslide", "act == 'study' and intelligence == 100",
event.once(), event.depends("borrow_pen"))
# She makes us cookies.
$ event("cookies", "act == 'study'",
event.once(), event.depends("bookslide"))
# Her solo ending.
$ event("gg_confess", "act == 'class'",
event.once(), event.depends("cookies"))
label gg_studying:
"I head to the library, to get some studying done."
"The glasses girl is there, but she's busy reading a book, taking
notes as she does so."
"I decide not to disturb her, and instead start reading my own
book."
$ intelligence += 10
return
label borrow_pen:
"I head to the library, to get some studying done."
"The glasses girl is there, but she's busy reading a book."
"I decide not to disturb her, and instead start reading my own
book."
"Suddenly, I feel a tap on my shoulder."
"I look up, and see the glasses girl standing right next to me."
gg "Excuse me, but can I borrow your pen?"
gg "Mine ran out of ink."
"I dig through my bag, to find the pen I had stashed there."
"While I'm looking, I point out that she seems to come to the
library alot."
gg "Hm... I guess you're right."
gg "There's so much stuff here, and I want to know about it all."
gg "Surely, you must feel the same way, as you're here almost as
much as I am."
"I don't have the heart to tell her that I'm only here to study so
that I don't fail out."
"My hand brushes the pen, and I quickly pull it out and give it to
her."
gg "Thank you."
"She says, and she goes back to studying."
return
label gg_smiling:
"I head to the library, to get some studying done."
"The glasses girl is there, and smiles at me for a second before
turning back to her book."
"I decide not to disturb her, and instead start reading my own
book."
$ intelligence += 10
return
label bookslide:
"I head to the library, to get some studying done."
"The glasses girl is standing right by the entrance, putting a
book back into a bookcase containing science books."
"It looks quite old, and quite weak, as if it could break at any
time."
"Suddenly, I hear a loud crack come from the bookcase."
"Without thinking, I throw myself between the girl and the
bookcase, pushing her out of the way in the process."
"As the shelves fail one by one, I'm hit with large textbooks on
topics ranging from Astronomy to Zoology."
"She's safe, but I'm knocked off my feet by the falling books."
"Before the dust even settled, the girl with glasses realized what
happened and asked:"
gg "Are you alright?"
"I tell her that I am, all the while rubbing a bruise left by a
particularly large Physics book."
gg "You saved me."
"She points out. I shrug... I guess I did, but it's not like I'm a
hero or anything."
"She extends out her hand, I take it, and she helps me to get up."
gg "Wow... Um..."
"She doesn't know what to say."
"I suggest that we help clean up the mess, mostly to take her off
the spot."
"She agrees, and together we begin piling the books up into neat
piles."
return
label cookies:
"I head to the library, to get some studying done."
"The glasses girl is there, apparently waiting for me."
"She's holding a package in her hands."
gg "Here."
"She says, and she hands me the package."
"I take it from her, and open it."
"It contains fresh homemade cookies. Ginger snaps, I think."
gg "It's to thank you."
"She points out... She's probably not used to this. Especially
with guys."
"I take one of them, and stick it in my mouth."
"The taste is exquisite."
"It's perhaps one of the best cookies I've ever tasted."
"Of course it is. It's the only cookie I'd ever tasted that was
made for me by a beutiful girl."
"I tell her this... that it's delicious, not the girl part."
"But I do let slip that if I could eat these every day, I'd be the
happiest guy in the world."
"At this, she can only blush."
return
label gg_confess:
"I once again barely make it to class on time."
"I sit down, at my desk, and put some of my books into it."
"My hand brushes a folded sheet of paper, one I didn't remember
putting in there."
"It's a girl's handwriting... \"Meet me on the roof at lunch.\""
"That's all it says... no signature or anything."
"Lunch is a few hours away, but the time passes like a blur."
"It's all I can do to avoid racing up to the roof... but I give it
some time anyway."
"It wouldn't make sense for me to get there first."
"I let two minutes elapse before leaving the classroom, and then
slowly walk the flights of stairs up to the roof."
"Standing there, I find the glasses girl."
"She's holding what looks like a homemade lunch... big enough for
two."
"I look at it, then her, then remember what I had said after she
made me the cookies."
"Finally, I ask her... \"Does this mean?\""
"She nods. It's all the confirmation I need."
"I sit down next to my new girlfriend... and together we start
eating her lunch."
"I'm probably the happiest guy in the world."
"But I still have to find out her name."
".:. Ending 1."
$ renpy.full_restart()
init:
$ event("catchme", "act == 'exercise'",
event.depends('introduction'), event.once())
$ event("cantcatchme", "act == 'exercise'",
event.depends('catchme'), event.solo(), priority=190)
$ event("caughtme", "act == 'exercise' and strength >= 50",
event.depends('catchme'), event.once())
$ event("together", "act == 'exercise' and strength >= 50",
event.depends('caughtme'), event.solo(), priority=180)
$ event("apart", "act == 'exercise' and strength < 50",
event.depends('caughtme'), event.solo(), priority=180)
$ event("pothole", "act == 'exercise' and strength >= 100",
event.depends('caughtme'), event.once())
$ event("dontsee", "act == 'exercise'",
event.depends('pothole'), event.solo(), priority=170)
$ event("sg_confess", "act == 'class'",
event.depends('dontsee'), event.once())
label catchme:
"I decide to go out for a run, to keep myself in shape."
"As I'm running through the town, I see a girl."
"She's one of the twins who transferred into my class."
"She waves, and comes over to me."
sg "I didn't know you were a runner."
"I point out that I'm not really a runner... I just run a little
bit at a time."
"I ask her if she wants to run with me for a while."
sg "As if! You couldn't keep up with me."
"I point out that I probably can."
sg "Well, let's see."
"We set off running, but she quickly pulls past me."
sg "See? Well, maybe we can try it again when you're a bit
faster."
sg "Until then, later."
"Even though I'm jogging, she pulls away as if it is nothing."
return
label cantcatchme:
"I'm out running again, when the sporty girl catches up to me."
sg "Still at it?"
sg "Well, keep up the good work. One day you'll be as fast as me!"
sg "Well, maybe."
"She pulls out past me, and disappears into the distance. One day
I'll catch up to her."
$ strength += 10
return
label caughtme:
"I'm out running again, when the sporty girl catches up to me."
sg "Still at it?"
sg "Well, keep up the good work. One day you'll be as fast as me!"
sg "Well, maybe."
"Today, however, I'm not about to let this stand unchallenged."
"I break out into a run, and for the first time ever, I keep up
with her."
"We both run, neck and neck, me keeping up with her."
"Finally, she starts slowing down, and we come to a stop
together."
sg "Not bad."
"She pauses to catch her breath."
sg "You've been practicing, and it really shows."
sg "You've finally become fast enough to run with me."
"I nod, accepting her praise."
sg "We should do this more often... it's better to run with
someone else, to keep the challenge up."
"I nod again."
sg "Well, shall we go?"
"I nod a third time, and we take off, running side by side."
$ strength += 10
return
label together:
"I start running, and meet up with the sporty girl as she passes
the street in front of my house."
"She's still better than me... she's been running for over a mile
before reaching this point."
"Still, I can keep up with her for the rest of the run. And that's
not bad."
$ strength += 10
return
label apart:
"I start running, and meet up with the sporty girl as she passes
the street in front of my house."
"I try to keep up with her, but she pulls away from me."
"When she's a block away, she slows down and lets me catch up."
sg "It's your own fault... this is what you get for not
practicing."
"She's right, of course, and I redouble my efforts to try to keep
up with her."
$ strength += 10
return
label pothole:
"I start running, and meet up with the sporty girl as she passes
the street in front of my house."
"We run together for several miles."
"I think about how much I've improved in our time together."
"And, although she probably won't admit it, I think she's improved
as well."
"I guess a little friendly competition is usually for the best."
"A small yelp pulls me out of my thought."
sg "Ow!"
"The sporty girl sits down, grabbing her ankle."
"I ask her what happened."
sg "I... hit a... pothole. Twisted my... ankle."
"I wince in sympathy."
"We wait a bit. I'm not sure what to do."
"Finally, I ask her if she can walk on it."
"She tries for a bit, but then winces in pain."
sg "No, I don't think so."
"I realize that we can't stay here."
"And so, I crouch down and motion for her to climb up onto my
back."
sg "What are you doing?"
"I explain that she can't stay out in the middle of the street
forever, and she won't get any help until I can get her home."
"And the only way to do that is for me to carry her."
"She accepts this, and climbs up onto my back."
"She wraps her arms around my neck, and I place my hands
underneath her to make a seat."
"I stand up, and start carrying her home."
return
label dontsee:
"I go running again."
"But this time, I don't see the sporty girl."
"I finish the course that we usually take, but it's not the same
without her."
return
label sg_confess:
"I once again barely make it to class on time."
"I sit down, at my desk, and put some of my books into it."
"My hand brushes a folded sheet of paper, one I didn't remember
putting in there."
"It's a girl's handwriting... \"Meet me on the roof at lunch.\""
"That's all it says... no signature or anything."
"Lunch is a few hours away, but the time passes like a blur."
"It's all I can do to avoid racing up to the roof... but I give it
some time anyway."
"It wouldn't make sense for me to get there first."
"I let two minutes elapse before leaving the classroom, and then
slowly walk the flights of stairs up to the roof."
"Standing there, I find the sporty girl."
"She's leaning on a crutch."
"I look at it for a second, and she notices that."
sg "I went to the doctor, and he gave me this."
sg "Looks like we won't be running together for a while."
"I nod."
sg "And that's why I asked you here."
sg "I couldn't stand the though of not seeing you for a few
weeks."
"I search my feelings, and realize I feel the same way."
sg "So I thought..."
"She doesn't say it... she doesn't need to."
"We both know how we feel about each other."
"And with that, we went from being running partners to partners in
a deeper sense."
"Now if I only knew her name..."
".:. Ending 2."
$ renpy.full_restart()
init:
# This needs to be higher-priority than either girl's ending.
$ event('both_confess', 'act == "class"',
event.depends("dontsee"), event.depends("cookies"),
event.once(), priority = 50)
label both_confess:
"I once again barely make it to class on time."
"I sit down, at my desk, and put some of my books into it."
"My hand brushes a folded sheet of paper, then another."
"I take the first one out, and read it."
"It's a girl's handwriting... \"Meet me on the roof at lunch.\""
"That's all it says... no signature or anything."
"I take a look at the second one, and it says the same thing."
"Sure, the handwriting is a little different, but..."
"Lunch is a few hours away, but the time passes like a blur."
"It's all I can do to avoid racing up to the roof... but I give it
some time anyway."
"I let two minutes elapse before leaving the classroom, and then
slowly walk the flights of stairs up to the roof."
"Standing there are the twins."
"Both of them."
"As in, the two notes came from the two twins."
"I ask them what they are doing there, feigning ignorance."
sg "Well, I invited you up here to confess to you..."
sg "... and then I found out that my sister here was about to do
the same thing."
gg "{size=-4}...I was...{/size}"
sg "When we found out, we were quite shocked, but after comparing
notes, we decide what we're going to do..."
"I quickly run through the possibilities in my head."
"The best cases involve them never talking to me again."
"The worst cases involve me being thrown off the roof."
bg "We're going to share you!"
"Eh?"
"That wasn't something I considered."
"Each of the girls grabs onto one of my arms."
"I don't know what the future holds for us..."
"... and I don't know if this will work out."
"But I do know that one day I will work up the courage to find out
their names."
".:. Ending 3."
$ renpy.full_restart()
-163
View File
@@ -1,163 +0,0 @@
# This is the main program. This can be changed quite a bit to
# customize it for your program... But remember what you do, so you
# can integrate with a new version of DSE when it comes out.
# Set up a default theme.
init python:
register_stat("Strength", "strength", 10, 100)
register_stat("Intelligence", "intelligence", 10, 100)
dp_period("Morning", "morning_act")
dp_choice("Attend Class", "class")
dp_choice("Cut Class", "cut")
dp_period("Afternoon", "afternoon_act")
dp_choice("Study", "study")
dp_choice("Hang Out", "hang")
dp_period("Evening", "evening_act")
dp_choice("Exercise", "exercise")
dp_choice("Play Games", "play")
# This is the entry point into the game.
label start:
# Initialize the default values of some of the variables used in
# the game.
$ day = 0
# Show a default background.
scene black
# The script here is run before any event.
"In case you're just tuning in, here's the story of my life to
date."
"I'm a guy in the second year of high school."
"I'm not good at sports or school or even something as simple as
remembering peoples names."
"In short, I am your usual random loser guy."
"And this is my story..."
# We jump to day to start the first day.
jump day
# This is the label that is jumped to at the start of a day.
label day:
# Increment the day it is.
$ day += 1
# We may also want to compute the name for the day here, but
# right now we don't bother.
"It's day [day]."
# Here, we want to set up some of the default values for the
# day planner. In a more complicated game, we would probably
# want to add and remove choices from the dp_ variables
# (especially dp_period_acts) to reflect the choices the
# user has available to him.
$ morning_act = None
$ afternoon_act = None
$ evening_act = None
# Now, we call the day planner, which may set the act variables
# to new values. We call it with a list of periods that we want
# to compute the values for.
call day_planner(["Morning", "Afternoon", "Evening"])
# We process each of the three periods of the day, in turn.
label morning:
# Tell the user what period it is.
centered "Morning"
# Set these variables to appropriate values, so they can be
# picked up by the expression in the various events defined below.
$ period = "morning"
$ act = morning_act
# Ensure that the stats are in the proper range.
$ normalize_stats()
# Execute the events for the morning.
call events_run_period
# That's it for the morning, so we fall through to the
# afternoon.
label afternoon:
# It's possible that we will be skipping the afternoon, if one
# of the events in the morning jumped to skip_next_period. If
# so, we should skip the afternoon.
if check_skip_period():
jump evening
# The rest of this is the same as for the morning.
centered "Afternoon"
$ period = "afternoon"
$ act = afternoon_act
$ normalize_stats()
call events_run_period
label evening:
# The evening is the same as the afternoon.
if check_skip_period():
jump night
centered "Evening"
$ period = "evening"
$ act = evening_act
$ normalize_stats()
call events_run_period
label night:
# This is now the end of the day, and not a period in which
# events can be run. We put some boilerplate end-of-day text
# in here.
centered "Night"
"It's getting late, so I decide to go to sleep."
# We call events_end_day to let it know that the day is done.
call events_end_day
# And we jump back to day to start the next day. This goes
# on forever, until an event ends the game.
jump day
# This is a callback that is called by the day planner. One of the
# uses of this is to show the statistics to the user.
label dp_callback:
# Add in a line of dialogue asking the question that's on
# everybody's mind.
$ narrator("What should I do today?", interact=False)
$ display_stats()
return
-249
View File
@@ -1,249 +0,0 @@
## This file contains some of the options that can be changed to customize
## your Ren'Py game. It only contains the most common options... there
## is quite a bit more customization you can do.
##
## Lines beginning with two '#' marks are comments, and you shouldn't
## uncomment them. Lines beginning with a single '#' mark are
## commented-out code, and you may want to uncomment them when
## appropriate.
init -1 python hide:
## Should we enable the use of developer tools? This should be
## set to False before the game is released, so the user can't
## cheat using developer tools.
config.developer = True
## These control the width and height of the screen.
config.screen_width = 800
config.screen_height = 600
## This controls the title of the window, when Ren'Py is
## running in a window.
config.window_title = u"DSE Demo Game"
#########################################
# Layouts
## This enables the use of an in-game menu that is made out of
## buttons.
layout.button_menu()
#########################################
# Themes
## We then want to call a theme function. themes.roundrect is
## a theme that features the use of rounded rectangles. It's
## the only theme we currently support.
##
## The theme function takes a number of parameters that can
## customize the color scheme.
theme.roundrect(
## The color of an idle widget face.
widget = "#003c78",
## The color of a focused widget face.
widget_hover = "#0050a0",
## The color of the text in a widget.
widget_text = "#c8ffff",
## The color of the text in a selected widget. (For
## example, the current value of a preference.)
widget_selected = "#ffd080",
## The color of a disabled widget face.
disabled = "#404040",
## The color of disabled widget text.
disabled_text = "#c8c8c8",
## The color of informational labels.
label = "#ffffff",
## The color of a frame containing widgets.
frame = "#6496c8",
## If this is True, the in-game window is rounded. If False,
## the in-game window is square.
rounded_window = False,
## The background of the main menu. This can be a color
## beginning with '#', or an image filename. The latter
## should take up the full height and width of the screen.
mm_root = "#dcebff",
## The background of the game menu. This can be a color
## beginning with '#', or an image filename. The latter
## should take up the full height and width of the screen.
gm_root = "#dcebff",
## And we're done with the theme. The theme will customize
## various styles, so if we want to change them, we should
## do so below.
)
theme.outline_bars(
inside="#fff",
idle="#003c78",
hover="#0050a0")
#########################################
## These settings let you customize the window containing the
## dialogue and narration, by replacing it with an image.
## The background of the window. In a Frame, the two numbers
## are the size of the left/right and top/bottom borders,
## respectively.
# style.window.background = Frame("frame.png", 12, 12)
## Margin is space surrounding the window, where the background
## is not drawn.
# style.window.left_margin = 6
# style.window.right_margin = 6
# style.window.top_margin = 6
# style.window.bottom_margin = 6
## Padding is space inside the window, where the background is
## drawn.
# style.window.left_padding = 6
# style.window.right_padding = 6
# style.window.top_padding = 6
# style.window.bottom_padding = 6
## This is the minimum height of the window, including the margins
## and padding.
# style.window.yminimum = 250
#########################################
## This lets you change the placement of the main menu.
## The way placement works is that we find an anchor point
## inside a displayable, and a position (pos) point on the
## screen. We then place the displayable so the two points are
## at the same place.
## An anchor/pos can be given as an integer or a floating point
## number. If an integer, the number is interpreted as a number
## of pixels from the upper-left corner. If a floating point,
## the number is interpreted as a fraction of the size of the
## displayable or screen.
# style.mm_menu_frame.xpos = 0.5
# style.mm_menu_frame.xanchor = 0.5
# style.mm_menu_frame.ypos = 0.75
# style.mm_menu_frame.yanchor = 0.5
#########################################
## These let you customize the default font used for text in Ren'Py.
## The file containing the default font.
# style.default.font = "DejaVuSans.ttf"
## The default size of text.
# style.default.size = 22
## Note that these only change the size of some of the text. Other
## buttons have their own styles.
#########################################
## These settings let you change some of the sounds that are used by
## Ren'Py.
## Set this to False if the game does not have any sound effects.
config.has_sound = True
## Set this to False if the game does not have any music.
config.has_music = True
## Set this to False if the game does not have voicing.
config.has_voice = True
## Sounds that are used when button and imagemaps are clicked.
# style.button.activate_sound = "click.wav"
# style.imagemap.activate_sound = "click.wav"
## Sounds that are used when entering and exiting the game menu.
# config.enter_sound = "click.wav"
# config.exit_sound = "click.wav"
## A sample sound that can be played to check the sound volume.
# config.sample_sound = "click.wav"
## Music that is played while the user is at the main menu.
# config.main_menu_music = "main_menu_theme.ogg"
#########################################
## Transitions.
## Used when entering the game menu from the game.
config.enter_transition = None
## Used when exiting the game menu to the game.
config.exit_transition = None
## Used between screens of the game menu.
config.intra_transition = None
## Used when entering the game menu from the main menu.
config.main_game_transition = None
## Used when returning to the main menu from the game.
config.game_main_transition = None
## Used when entering the main menu from the splashscreen.
config.end_splash_transition = None
## Used when entering the main menu after the game has ended.
config.end_game_transition = None
## Used when a game is loaded.
config.after_load_transition = None
#########################################
## Default values of Preferences.
## Note: These options are only evaluated the first time a
## game is run. To have them run a second time, delete
## game/saves/persistent
## Should we start in fullscreen mode?
config.default_fullscreen = False
## The default text speed in characters per second. 0 is infinite.
config.default_text_cps = 0
-88
View File
@@ -1,88 +0,0 @@
init -100 python:
style.stats_frame = Style(style.frame)
style.stats_vbox = Style(style.vbox)
style.stats_label = Style(style.label)
style.stats_label_text = Style(style.label_text)
style.stat_side = Style(style.default)
style.stat_label = Style(style.label)
style.stat_label_text = Style(style.label_text)
style.stat_bar = Style(style.bar)
style.stat_value_label = Style(style.label)
style.stat_value_label_text = Style(style.label_text)
__dse_stats = [ ]
class __Stat(object):
def __init__(self, name, var, default, max):
self.name = name
self.var = var
self.default = default
self.max = max
def __init_stats():
for s in __dse_stats:
setattr(store, s.var, s.default)
config.start_callbacks.append(__init_stats)
def register_stat(name, var, default, max):
__dse_stats.append(__Stat(name, var, default, max))
def normalize_stats():
for s in __dse_stats:
v = getattr(store, s.var)
if v > s.max:
v = s.max
if v < 0:
v = 0
setattr(store, s.var, v)
def display_stats(name=True, bar=True, value=True, max=True):
normalize_stats()
ui.window(style=style.stats_frame)
ui.vbox(style=style.stats_vbox)
layout.label("Statistics", "stats")
for s in __dse_stats:
v = getattr(store, s.var)
ui.side(['l', 'r', 'c'], style=style.stat_side)
if name:
layout.label(s.name, "stat")
else:
ui.null()
if value and max:
layout.label("%d/%d" % (v, s.max), "stat_value")
elif value:
layout.label("%d" % (v,), "stat_value")
elif max:
layout.label("%d" % (max,), "stat_value")
else:
ui.null()
if bar:
ui.bar(s.max, v, style=style.stat_bar)
else:
ui.null()
ui.close()
ui.close()
-49
View File
@@ -1,49 +0,0 @@
# This file contains styles for the day planner.
init python:
# Place the day planner.
style.dp_frame.ypos = 120
style.dp_frame.yanchor = 0.0
style.dp_frame.xalign = 0.5
# Spacing betweeen the choices and the done button.
style.dp_vbox.box_spacing = 20
# Spacing between the choice columns.
style.dp_hbox.box_spacing = 20
# Center the choices.
style.dp_choices.xalign = 0.5
# Center the label of each choice.
style.dp_label.xalign = 0.5
style.dp_label_text.text_align = 0.5
# Make each choice button the same size, and centered.
style.dp_choice_button.size_group = "dp_choice"
style.dp_choice_button.xalign = 0.5
# Center the done button.
style.dp_done_button.xalign = 0.5
# Put a margin on the stats frame.
style.stats_frame.xmargin = 10
style.stats_frame.ymargin = 5
# Space between the label and the stats.
style.stats_vbox.box_first_spacing = 10
# Put blank space around each stat name, and right-justify.
style.stat_label.xminimum = 140
style.stat_label_text.xalign = 1.0
style.stat_label.xmargin = 5
# Put blank space around each stat value, and right-justify.
style.stat_value_label.xminimum = 100
style.stat_value_label_text.xalign = 1.0
# Center the stat bar vertically.
style.stat_bar.yalign = 0.5
+1 -1
View File
@@ -112,7 +112,7 @@ display: none;
<dd>Toggles fullscreen mode</dd>
<dt>S</dt>
<dd>Takes a screenshot, saving it in a file named screenshotxxxx.png, where xxxx is a serial number.</dd>
<dt>Alt-H, Command-H</dt>
<dt>Alt-M, Command-H</dt>
<dd>Hides (iconifies) the window.</dd>
<dt>Alt-F4, Command-Q</dt>
<dd>Quits the game.</dd>
+36
View File
@@ -0,0 +1,36 @@
label add_file:
python hide:
import os
import codecs
filename = interface.input(_("FILENAME"), _("Enter the name of the script file to create."), filename="withslash", cancel=Jump("navigation"))
if "." in filename and not filename.endswith(".rpy"):
interface.error(_("The filename must have the .rpy extension."), label="navigation")
elif "." not in filename:
filename += ".rpy"
path = os.path.join(project.current.gamedir, filename)
dir = os.path.dirname(path)
if os.path.exists(path):
interface.error(_("The file already exists."), label="navigation")
contents = u"\uFEFF"
contents += _("# Ren'Py automatically loads all script files ending with .rpy. To use this\n# file, define a label and jump to it from another file.\n")
contents += "\n"
try:
os.makedirs(dir)
except:
pass
contents = u"\uFEFF"
contents += _("# Ren'Py automatically loads all script files ending with .rpy. To use this\n# file, define a label and jump to it from another file.\n")
with open(path, "wb") as f:
f.write(contents.encode("utf-8"))
jump navigation_refresh
+1 -1
View File
@@ -5,7 +5,7 @@
import struct
import sys
import array
import pefile
import pefile # @UnresolvedImport
# This class performs various operations on memory-loaded binary files,
# including modifications.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

+1 -1
View File
@@ -429,7 +429,7 @@ screen choose_theme:
# Preview
frame:
style "l_default"
background Frame("pattern.png", 0, 0, tile=True)
background Frame(PATTERN, 0, 0, tile=True)
xpadding 5
ypadding 5
+126 -45
View File
@@ -30,6 +30,8 @@ init python in distribute:
import os
import io
import re
import plistlib
import time
match_cache = { }
@@ -45,7 +47,7 @@ init python in distribute:
regexp += r'.*'
pattern = pattern[2:]
elif pattern[0] == "*":
regexp += r'[^/]*'
regexp += r'[^/]*/?'
pattern = pattern[1:]
elif pattern[0] == '[':
regexp += r'['
@@ -150,6 +152,26 @@ init python in distribute:
return rv
def filter_empty(self):
"""
Makes a deep copy of this file list with empty directories
omitted.
"""
rv = FileList()
needed_dirs = set()
for i in reversed(self):
if (not i.directory) or (i.name in needed_dirs):
rv.insert(0, i.copy())
directory, _sep, _filename = i.name.rpartition("/")
needed_dirs.add(directory)
return rv
@staticmethod
def merge(l):
"""
@@ -322,10 +344,15 @@ init python in distribute:
self.reporter.info(_("Scanning Ren'Py files..."))
self.scan_and_classify(config.renpy_base, build["renpy_patterns"])
# Add Python (with the same name as our executables)
self.add_python()
# Build the mac app.
self.add_mac_files()
# Add generated/special files.
if not build['renpy']:
self.add_renpy_files()
self.add_mac_files()
self.add_windows_files()
# Assign the x-bit as necessary.
@@ -417,7 +444,7 @@ init python in distribute:
self.project.make_tmp()
return os.path.join(self.project.tmp, name)
def add_file(self, file_list, name, path):
def add_file(self, file_list, name, path, executable=False):
"""
Adds a file to the file lists.
@@ -437,7 +464,7 @@ init python in distribute:
if isinstance(file_list, basestring):
file_list = file_list.split()
f = File(name, path, False, False)
f = File(name, path, False, executable)
for fl in file_list:
self.file_lists[fl].append(f)
@@ -487,51 +514,104 @@ init python in distribute:
self.add_file("all", "game/script_version.rpyc", os.path.join(config.gamedir, "script_version.rpyc"))
self.add_file("all", "renpy/LICENSE.txt", os.path.join(config.renpy_base, "LICENSE.txt"))
def write_plist(self):
display_name = self.build['display_name']
executable_name = self.executable_name
version = self.build['version']
plist = dict(
CFBundleDevelopmentRegion="English",
CFBundleDisplayName=display_name,
CFBundleExecutable=executable_name,
CFBundleIconFile="icon",
CFBundleInfoDictionaryVersion="6.0",
CFBundleName=display_name,
CFBundlePackageType="APPL",
CFBundleShortVersionString=version,
CFBundleVersion="1.0.{0}".format(int(time.time())),
CFBundleDocumentTypes = [
{
"CFBundleTypeOSTypes" : [ "****", "fold", "disk" ],
"CFBundleTypeRole" : "Viewer",
},
],
UTExportedTypeDeclarations = [
{
"UTTypeConformsTo" : [ "public.python-script" ],
"UTTypeDescription" : "Ren'Py Script",
"UTTypeIdentifier" : "org.renpy.rpy",
"UTTypeTagSpecification" : { "public.filename-extension" : [ "rpy" ] }
},
],
)
rv = self.temp_filename("Info.plist")
plistlib.writePlist(plist, rv)
return rv
def add_python(self):
if self.build['renpy']:
windows = 'binary'
linux = 'binary'
mac = 'binary'
else:
windows = 'windows'
linux = 'linux'
mac = 'mac'
self.add_file(
linux,
"lib/linux-i686/" + self.executable_name,
os.path.join(config.renpy_base, "lib/linux-i686/pythonw"),
True)
self.add_file(
linux,
"lib/linux-x86_64/" + self.executable_name,
os.path.join(config.renpy_base, "lib/linux-x86_64/pythonw"),
True)
self.add_file(
mac,
"lib/darwin-x86_64/" + self.executable_name,
os.path.join(config.renpy_base, "lib/darwin-x86_64/pythonw"),
True)
self.add_file(
windows,
"lib/windows-i686/" + self.executable_name + ".exe",
os.path.join(config.renpy_base, "lib/windows-i686/pythonw.exe"))
def add_mac_files(self):
"""
Add mac-specific files to the distro.
"""
# Rename the executable.
self.add_file("mac", "renpy.app/Contents/MacOS/" + self.executable_name, os.path.join(config.renpy_base, "renpy.app/Contents/MacOS/Ren'Py Launcher"))
# Update the plist file.
quoted_name = self.executable_name.replace("&", "&amp;").replace("<", "&lt;")
fn = self.temp_filename("Info.plist")
with io.open(os.path.join(config.renpy_base, "renpy.app/Contents/Info.plist"), "r", encoding="utf-8") as old:
data = old.read()
data = data.replace("Ren'Py Launcher", quoted_name)
with io.open(fn, "w", encoding="utf-8") as new:
new.write(data)
self.add_file("mac", "renpy.app/Contents/Info.plist", fn)
# Update the launcher script.
quoted_name = self.executable_name.replace("\"", "\\\"")
fn = self.temp_filename("launcher.py")
with io.open(os.path.join(config.renpy_base, "renpy.app/Contents/Resources/launcher.py"), "r", encoding="utf-8") as old:
data = old.read()
data = data.replace("Ren'Py Launcher", quoted_name)
with io.open(fn, "w", encoding="utf-8") as new:
new.write(data)
self.add_file("mac", "renpy.app/Contents/Resources/launcher.py", fn)
# Icon file.
custom_fn = os.path.join(self.project.path, "icon.icns")
default_fn = os.path.join(config.renpy_base, "renpy.app/Contents/Resources/launcher.icns")
if os.path.exists(custom_fn):
self.add_file("mac", "renpy.app/Contents/Resources/launcher.icns", custom_fn)
if self.build['renpy']:
filelist = "binary"
else:
self.add_file("mac", "renpy.app/Contents/Resources/launcher.icns", default_fn)
filelist = "mac"
contents = self.app + "/Contents"
plist_fn = self.write_plist()
self.add_file(filelist, contents + "/Info.plist", plist_fn)
self.add_file(filelist, contents + "/MacOS/" + self.executable_name, os.path.join(config.renpy_base, "renpy.sh"))
custom_fn = os.path.join(self.project.path, "icon.icns")
default_fn = os.path.join(config.renpy_base, "launcher/icon.icns")
if os.path.exists(custom_fn):
icon_fn = custom_fn
else:
icon_fn = default_fn
self.add_file(filelist, contents + "/Resources/icon.icns", icon_fn)
def add_windows_files(self):
"""
@@ -575,9 +655,7 @@ init python in distribute:
parts = fn.split('/')
p = parts[0]
if p == "renpy.app":
p = self.app
elif p == "renpy.exe":
if p == "renpy.exe":
p = self.exe
elif p == "renpy.sh":
p = self.sh
@@ -617,6 +695,9 @@ init python in distribute:
fl = fl.copy()
fl.sort()
if self.build.get("exclude_empty_directories", True):
fl = fl.filter_empty()
# Write the update information.
update_files = [ ]
update_xbit = [ ]
+1
View File
@@ -342,6 +342,7 @@ init python in editor:
return
scripts = project.current.script_files()
scripts = [ i for i in scripts if not i.startswith("game/tl/") ]
scripts.sort(key=lambda fn : fn.lower())
for fn in [ "game/screens.rpy", "game/options.rpy", "game/script.rpy" ]:
+9 -8
View File
@@ -48,12 +48,12 @@ screen front_page:
# Projects list section - on left.
frame:
style "l_default"
style "l_projects"
xmaximum 300
right_margin 2
top_margin 20
bottom_margin 26
top_padding 20
bottom_padding 26
side "t c b":
@@ -125,7 +125,7 @@ screen front_page_project:
frame style "l_label":
has hbox xfill True
text "[p.name!q]" style "l_label_text"
label "Active Project" style "l_alternate"
label _("Active Project") style "l_alternate"
grid 2 1:
xfill True
@@ -150,9 +150,9 @@ screen front_page_project:
frame style "l_indent":
has vbox
textbutton _("script.rpy") action editor.Edit("game/script.rpy", check=True)
textbutton _("options.rpy") action editor.Edit("game/options.rpy", check=True)
textbutton _("screens.rpy") action editor.Edit("game/screens.rpy", check=True)
textbutton "script.rpy" action editor.Edit("game/script.rpy", check=True)
textbutton "options.rpy" action editor.Edit("game/options.rpy", check=True)
textbutton "screens.rpy" action editor.Edit("game/screens.rpy", check=True)
textbutton _("All script files") action editor.EditAll()
add SPACER
@@ -184,7 +184,8 @@ screen front_page_project:
if ability.can_distribute:
textbutton _("Build Distributions") action Jump("build_distributions")
textbutton _("Generate Translations") action Jump("translate")
label main_menu:
return
+10 -11
View File
@@ -5,6 +5,7 @@
# Interface actions.
init python in interface:
from store import OpenURL, config, Return
import store
import os.path
import contextlib
@@ -173,13 +174,13 @@ screen launcher_input:
add SPACER
input style "l_default" size 24 xalign 0.5 default default color "#d86b45"
input style "l_default" size 24 xalign 0.5 default default color INPUT_COLOR
if filename:
add SPACER
text _("Due to package format limitations, non-ASCII file and directory names are not allowed.")
label _("[title]") style "l_info_label" text_color "#d19753"
label _("[title]") style "l_info_label" text_color QUESTION_COLOR
if cancel:
@@ -244,7 +245,7 @@ init python in interface:
the message.
"""
common(_("ERROR"), "#d15353", message=message, submessage=submessage, back=True, **kwargs)
common(_("ERROR"), store.ERROR_COLOR, message=message, submessage=submessage, back=True, **kwargs)
if label:
@@ -298,7 +299,7 @@ init python in interface:
continue
if filename:
if ("\\" in rv) or ("/" in rv):
if filename and (filename != "withslash") and (("\\" in rv) or ("/" in rv)):
error(_("File and directory names may not contain / or \\."), label=None)
continue
@@ -310,8 +311,6 @@ init python in interface:
return rv
def info(message, submessage=None, pause=True, **kwargs):
"""
Displays an informational message to the user. The user will be asked to click to
@@ -328,9 +327,9 @@ init python in interface:
"""
if pause:
common(_("INFORMATION"), "#545454", message, submessage, continue_=True, **kwargs)
common(_("INFORMATION"), store.INFO_COLOR, message, submessage, continue_=True, **kwargs)
else:
common(_("INFORMATION"), "#545454", message, submessage, pause=0, **kwargs)
common(_("INFORMATION"), store.INFO_COLOR, message, submessage, pause=0, **kwargs)
def interaction(title, message, submessage=None, **kwargs):
@@ -348,7 +347,7 @@ init python in interface:
An optional sub message.
"""
common(title, "#d19753", message, submessage, pause0=True, **kwargs)
common(title, store.INTERACTION_COLOR, message, submessage, pause0=True, **kwargs)
def processing(message, submessage=None, complete=None, total=None, **kwargs):
"""
@@ -371,7 +370,7 @@ init python in interface:
the message.
"""
common(_("PROCESSING"), "#545454", message, submessage, pause0=True, complete=complete, total=total, **kwargs)
common(_("PROCESSING"), store.INTERACTION_COLOR, message, submessage, pause0=True, complete=complete, total=total, **kwargs)
def yesno(message, yes=Return(True), no=Return(False), **kwargs):
@@ -388,5 +387,5 @@ init python in interface:
The action to perform if the user answer no.
"""
return common(_("QUESTION"), "#d19753", message, yes=yes, no=no, **kwargs)
return common(_("QUESTION"), store.QUESTION_COLOR, message, yes=yes, no=no, **kwargs)
+5 -1
View File
@@ -136,7 +136,7 @@ init python in navigation:
screen navigation:
frame:
style_group "l"
style "l_root"
@@ -218,6 +218,10 @@ screen navigation:
if group_name is not None:
add SPACER
if persistent.navigation == "file":
add SPACER
textbutton _("+ Add script file") action Jump("add_file") style "l_button"
else:
fixed:
+17 -10
View File
@@ -174,7 +174,14 @@ init -1 python hide:
_game_menu_screen = None
config.underlay = [ ]
config.underlay = [
renpy.Keymap(
quit = renpy.quit_event,
iconify = renpy.iconify,
choose_renderer = renpy.curried_call_in_new_context("_choose_renderer"),
),
]
config.rollback_enabled = False
@@ -230,9 +237,9 @@ init python:
# main source.
build.classify_renpy("renpy.py", "source")
build.classify_renpy("renpy/**", "source")
build.classify_renpy("common/**", "source")
# games.
build.classify_renpy("launcher/game/theme/", None)
build.classify_renpy("launcher/**", "source")
build.classify_renpy("template/**", "source")
build.classify_renpy("the_question/**", "source")
@@ -259,14 +266,12 @@ init python:
build.classify_renpy("module/pysdlsound/*.pyx", "source")
# all-platforms binary.
build.classify_renpy("lib/*/renpy", None)
build.classify_renpy("lib/*/renpy.exe", None)
build.classify_renpy("lib/**", "binary")
build.classify_renpy("python27.dll", "binary")
build.classify_renpy("msvcr90.dll", "binary")
build.classify_renpy("Microsoft.VC90.CRT.manifest", "binary")
build.classify_renpy("renpy.sh", "binary")
build.classify_renpy("renpy.app/**", "binary")
build.classify_renpy("renpy.exe", "binary")
build.classify_renpy("console.exe", "binary")
# renpy.app is now built from scratch from distribute.rpy.
# jedit rules.
build.classify_renpy("jedit/**", "jedit")
@@ -274,10 +279,12 @@ init python:
# editra rules.
build.classify_renpy("editra/", "editra-all")
build.classify_renpy("editra/Editra.edit.py", "editra-all")
build.classify_renpy("editra/Editra/**", "editra-linux")
build.classify_renpy("editra/Editra/**", "editra-linux editra-windows")
build.classify_renpy("editra/Editra-mac.app/**", "editra-mac")
build.classify_renpy("editra/Editra-win32/**", "editra-windows")
build.classify_renpy("editra/lib/**", "editra-windows")
build.classify_renpy("editra/editra.exe", "editra-windows")
# Executable rules.
build.executable("editra/Editra/Editra")
+92 -12
View File
@@ -7,16 +7,88 @@ init python in distribute:
import zipfile
import tarfile
import zlib
import struct
import stat
from zipfile import crc32
zlib.Z_DEFAULT_COMPRESSION = 9
class ZipFile(zipfile.ZipFile):
def write_with_info(self, zinfo, filename):
"""Put the bytes from filename into the archive under the name
arcname."""
if not self.fp:
raise RuntimeError(
"Attempt to write to ZIP archive that was already closed")
st = os.stat(filename)
isdir = stat.S_ISDIR(st.st_mode)
zinfo.file_size = st.st_size
zinfo.flag_bits = 0x00
zinfo.header_offset = self.fp.tell() # Start of header bytes
self._writecheck(zinfo)
self._didModify = True
if isdir:
zinfo.file_size = 0
zinfo.compress_size = 0
zinfo.CRC = 0
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
self.fp.write(zinfo.FileHeader())
return
with open(filename, "rb") as fp:
# Must overwrite CRC and sizes with correct data later
zinfo.CRC = CRC = 0
zinfo.compress_size = compress_size = 0
zinfo.file_size = file_size = 0
self.fp.write(zinfo.FileHeader())
if zinfo.compress_type == zipfile.ZIP_DEFLATED:
cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
zlib.DEFLATED, -15)
else:
cmpr = None
while 1:
buf = fp.read(1024 * 1024)
if not buf:
break
file_size = file_size + len(buf)
CRC = crc32(buf, CRC) & 0xffffffff
if cmpr:
buf = cmpr.compress(buf)
compress_size = compress_size + len(buf)
self.fp.write(buf)
if cmpr:
buf = cmpr.flush()
compress_size = compress_size + len(buf)
self.fp.write(buf)
zinfo.compress_size = compress_size
else:
zinfo.compress_size = file_size
zinfo.CRC = CRC
zinfo.file_size = file_size
# Seek backwards and write CRC and file sizes
position = self.fp.tell() # Preserve current position in file
self.fp.seek(zinfo.header_offset + 14, 0)
self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
zinfo.file_size))
self.fp.seek(position, 0)
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
class ZipPackage(object):
"""
A class that creates a zip file.
"""
def __init__(self, filename):
self.zipfile = zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED)
self.zipfile = ZipFile(filename, "w", zipfile.ZIP_DEFLATED)
def add_file(self, name, path, xbit):
@@ -31,18 +103,26 @@ init python in distribute:
zi.create_system = 3
if xbit:
zi.external_attr = long(0100777) << 16
zi.external_attr = long(0100755) << 16
else:
zi.external_attr = long(0100666) << 16
zi.external_attr = long(0100644) << 16
with open(path, "rb") as f:
data = f.read()
self.zipfile.writestr(zi, data)
self.zipfile.write_with_info(zi, path)
def add_directory(self, name, path):
return
if path is None:
return
zi = zipfile.ZipInfo(name + "/")
s = os.stat(path)
zi.date_time = time.gmtime(s.st_mtime)[:6]
zi.compress_type = zipfile.ZIP_STORED
zi.create_system = 3
zi.external_attr = (long(0040755) << 16) | 0x10
self.zipfile.write_with_info(zi, path)
def close(self):
self.zipfile.close()
@@ -66,13 +146,13 @@ init python in distribute:
else:
info = tarfile.TarInfo(name)
info.size = 0
info.mtime = time.time()
info.mtime = int(time.time())
info.type = tarfile.DIRTYPE
if xbit:
info.mode = 0777
info.mode = 0755
else:
info.mode = 0666
info.mode = 0644
info.uid = 1000
info.gid = 1000
+50 -6
View File
@@ -10,9 +10,25 @@ init python:
if persistent.windows_console is None:
persistent.windows_console = False
def scan_translations():
languages = renpy.known_languages()
if not languages:
return None
rv = [ ( "English", None) ]
for i in languages:
rv.append((i.title(), i))
return rv
screen preferences:
$ translations = scan_translations()
frame:
style_group "l"
style "l_root"
@@ -24,13 +40,12 @@ screen preferences:
label _("Launcher Preferences")
add HALF_SPACER
hbox:
frame:
style "l_indent"
xmaximum ONEHALF
xmaximum ONETHIRD
xfill True
has vbox
@@ -54,7 +69,8 @@ screen preferences:
else:
textbutton _("Not Set") action Jump("projects_directory_preference")
add SPACER
# Text editor selection.
@@ -96,7 +112,7 @@ screen preferences:
frame:
style "l_indent"
xmaximum ONEHALF
xmaximum ONETHIRD
xfill True
has vbox
@@ -131,7 +147,14 @@ screen preferences:
if renpy.windows:
textbutton _("Console output") style "l_checkbox" action ToggleField(persistent, "windows_console")
add SPACER
frame:
style "l_indent"
xmaximum ONETHIRD
xfill True
has vbox
add SEPARATOR2
frame:
@@ -143,8 +166,29 @@ screen preferences:
add HALF_SPACER
textbutton _("Open launcher as project") style "l_nonbox" action [ project.Select("launcher"), Jump("front_page") ]
textbutton _("Open launcher project") style "l_nonbox" action [ project.Select("launcher"), Jump("front_page") ]
if translations:
add SPACER
# Text editor selection.
add SEPARATOR2
frame:
style "l_indent"
yminimum 75
has vbox
text _("Language:")
add HALF_SPACER
# frame style "l_indent":
for tlname, tlvalue in translations:
textbutton tlname action Language(tlvalue) style "l_list"
textbutton _("Back") action Jump("front_page") style "l_left_button"
+73 -42
View File
@@ -5,13 +5,10 @@
init python:
try:
import EasyDialogs
except ImportError:
try:
import EasyDialogsWin as EasyDialogs
except:
EasyDialogs = None
import EasyDialogsWin as EasyDialogs
except:
EasyDialogs = None
import os
init python in project:
@@ -24,6 +21,7 @@ init python in project:
import json
import subprocess
import re
import tempfile
class Project(object):
@@ -52,10 +50,7 @@ init python in project:
self.load_data()
# The project's temporary directory.
self.tmp = os.path.join(self.path, "tmp")
# The path to the json dumpfile.
self.dump_filename = os.path.join(self.tmp, "navigation.json")
self.tmp = None
# This contains the result of dumping information about the game
# to disk.
@@ -64,6 +59,10 @@ init python in project:
# The mtime of the last dump file loaded.
self.dump_mtime = 0
def get_dump_filename(self):
self.make_tmp()
return os.path.join(self.tmp, "navigation.json")
def load_data(self):
try:
f = open(os.path.join(self.path, "project.json"), "rb")
@@ -98,10 +97,21 @@ init python in project:
yet.
"""
if self.tmp and os.path.isdir(self.tmp):
return
tmp = os.path.join(self.path, "tmp")
try:
os.mkdir(self.tmp)
os.mkdir(tmp)
except:
pass
if os.path.isdir(tmp):
self.tmp = tmp
return
self.tmp = tempfile.mkdtemp()
def temp_filename(self, filename):
"""
@@ -112,30 +122,58 @@ init python in project:
return os.path.join(self.tmp, filename)
def launch(self, args=[], wait=False):
"""
Launches the project.
`args`
Additional arguments to give to the project.
`wait`
If true, waits for the launched project to terminate before
continuing.
"""
self.make_tmp()
if renpy.renpy.windows and sys.argv[0].endswith(".exe"):
if persistent.windows_console:
cmd = [ os.path.join(config.renpy_base, "console.exe") ]
else:
cmd = [ os.path.join(config.renpy_base, "renpy.exe") ]
# Find the python executable to run.
executable_path = os.path.dirname(sys.executable)
if renpy.renpy.windows:
extension = ".exe"
else:
cmd = [ sys.executable, "-OO", sys.argv[0] ]
extension = ""
if persistent.windows_console:
executables = [ "python" + extension ]
else:
executables = [ "pythonw" + extension ]
executables.append(sys.executable)
for i in executables:
executable = os.path.join(executable_path, i)
if os.path.exists(executable):
break
else:
raise Exception("Python interpreter not found: %r", executables)
# Put together the basic command line.
cmd = [ executable, "-EOO", sys.argv[0] ]
cmd.append(self.path)
cmd.extend(args)
# Add flags to dump game info.
cmd.append("--json-dump")
cmd.append(self.dump_filename)
cmd.append(self.get_dump_filename())
if persistent.navigate_private:
cmd.append("--json-dump-private")
if persistent.navigate_library:
cmd.append("--json-dump-common")
# Launch the project.
with interface.error_handling("launching the project"):
cmd = [ renpy.fsencode(i) for i in cmd ]
@@ -150,26 +188,27 @@ init python in project:
loads it in iff it's newer than the one that's already loaded.
"""
if force or not os.path.exists(self.dump_filename):
self.make_tmp()
dump_filename = self.get_dump_filename()
if force or not os.path.exists(dump_filename):
if gui:
interface.processing(_("Ren'Py is scanning the project..."))
self.launch(["quit"], wait=True)
if not os.path.exists(self.dump_filename):
if not os.path.exists(dump_filename):
self.dump["error"] = True
return
file_mtime = os.path.getmtime(self.dump_filename)
file_mtime = os.path.getmtime(dump_filename)
if file_mtime == self.dump_mtime:
return
self.dump_mtime = file_mtime
try:
with open(self.dump_filename, "r") as f:
with open(dump_filename, "r") as f:
self.dump = json.load(f)
# add todo list to dump data
self.update_todos()
@@ -443,16 +482,13 @@ label choose_projects_directory:
python hide:
interface.interaction(_("PROJECTS DIRECTORY"), _("Please choose the projects directory."), _("This launcher will scan for projects in this directory, will create new projects in this directory, and will place built projects into this directory."))
interface.interaction(_("PROJECTS DIRECTORY"), _("Please choose the projects directory using the directory chooser.\n{b}The directory chooser may have opened behind this window.{/b}"), _("This launcher will scan for projects in this directory, will create new projects in this directory, and will place built projects into this directory."),)
path = persistent.projects_directory
if path:
default_path = path
else:
try:
default_path = os.path.dirname(os.path.abspath(config.renpy_base))
except:
@@ -471,27 +507,22 @@ label choose_projects_directory:
else:
try:
env = os.environ.copy()
if 'RENPY_ORIGINAL_LD_LIBRARY_PATH' in env:
env['LD_LIBRARY_PATH'] = env['RENPY_ORIGINAL_LD_LIBRARY_PATH']
cmd = [ "zenity", "--title=Select Projects Directory", "--file-selection", "--directory", "--filename=" + renpy.fsencode(default_path) ]
zen = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE)
choice = zen.stdout.read()
zen.wait()
cmd = [ "/usr/bin/python", os.path.join(config.gamedir, "tkaskdir.py"), renpy.fsencode(default_path) ]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
choice = p.stdout.read()
p.wait()
if choice:
path = renpy.fsdecode(choice[:-1])
path = renpy.fsdecode(choice)
except:
import traceback
traceback.print_exc()
path = None
interface.error(_("Ren'Py was unable to run zenity to choose the projects directory."), label=None)
interface.error(_("Ren'Py was unable to run python with tkinter to choose the projects directory."), label=None)
if path is None:
path = default_path
+1 -1
View File
@@ -1,4 +1,4 @@
init -999:
$ config.script_version = (6, 14, 1)
$ config.script_version = (6, 15, 7)
+81 -34
View File
@@ -1,14 +1,59 @@
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
# See LICENSE.txt for license details.
init python:
init -1 python:
# The color of non-interactive text.
TEXT = "#545454"
# Colors for buttons in various states.
IDLE = "#42637b"
HOVER = "#d86b45"
DISABLED = "#808080"
# Colors for reversed text buttons (selected list entries).
REVERSE_IDLE = "#78a5c5"
REVERSE_HOVER = "#d86b45"
REVERSE_TEXT = "#ffffff"
# Colors for the scrollbar thumb.
SCROLLBAR_IDLE = "#dfdfdf"
SCROLLBAR_HOVER = "#d86b45"
# An image used as a separator pattern.
PATTERN = "pattern.png"
# A displayable used for the background of everything.
BACKGROUND = "background.png"
# A displayable used for the background of windows
# containing commands, preferences, and navigation info.
WINDOW = Frame("window.png", 0, 0, tile=True)
# A displayable used for the background of the projects list.
PROJECTS_WINDOW = Null()
# A displayable used the background of information boxes.
INFO_WINDOW = "#f9f9f9"
# Colors for the titles of information boxes.
ERROR_COLOR = "#d15353"
INFO_COLOR = "#545454"
INTERACTION_COLOR = "#d19753"
QUESTION_COLOR = "#d19753"
# The color of input text.
INPUT_COLOR = "#d86b45"
init 1 python:
INDENT = 20
HALF_INDENT = 10
SCROLLBAR_SIZE = 16
SEPARATOR = Frame("pattern.png", 0, 0, tile=True, ymaximum=5, yalign=1.0)
SEPARATOR2 = Frame("pattern.png", 0, 0, tile=True, ymaximum=10, yalign=1.0)
SEPARATOR = Frame(PATTERN, 0, 0, tile=True, ymaximum=5, yalign=1.0)
SEPARATOR2 = Frame(PATTERN, 0, 0, tile=True, ymaximum=10, yalign=1.0)
SPACER_HEIGHT = 12
SPACER = Null(height=SPACER_HEIGHT)
@@ -16,16 +61,6 @@ init python:
HALF_SPACER_HEIGHT = 6
HALF_SPACER = Null(height=HALF_SPACER_HEIGHT)
# Colors.
TEXT = "#545454"
IDLE = "#42637b"
LIGHT_IDLE = "#78a5c5"
SCROLLBAR_IDLE = "#dfdfdf"
HOVER = "#d86b45"
WHITE = "#ffffff"
DISABLED = "#808080"
DANGER = "#d15353"
# FONTS/WEIGHTS
LIGHT = "Roboto-Light.ttf"
REGULAR = "Roboto-Regular.ttf"
@@ -36,7 +71,6 @@ init python:
TWOTHIRDS = 496
ONEHALF = 377
# Default style.
style.l_default = Style(style.default)
style.l_default.font = LIGHT
@@ -75,18 +109,21 @@ init python:
# The root frame. This contains everything but the bottom navigation, back
# button, and tooltip button.
style.l_root = Style(style.l_default)
style.l_root.background = "background.png"
style.l_root.background = BACKGROUND
style.l_root.xpadding = 10
style.l_root.top_padding = 64
style.l_root.bottom_padding = 128
# An inner window.
style.l_window = Style(style.l_default)
style.l_window.background = Frame("window.png", 0, 0, tile=True)
style.l_window.background = WINDOW
style.l_window.left_padding = 6
style.l_window.xfill = True
style.l_window.yfill = True
# Normal-sized labels.
style.l_label = Style(style.l_default)
style.l_label.xfill = True
@@ -128,7 +165,6 @@ init python:
style.l_small_text = Style(style.l_text)
style.l_small_text.size = 14
# Indents its contents.
style.l_indent = Style(style.l_default)
style.l_indent.left_margin = INDENT
@@ -141,13 +177,13 @@ init python:
style.l_list = Style(style.l_default)
style.l_list.left_padding = HALF_INDENT
style.l_list.xfill = True
style.l_list.selected_background = LIGHT_IDLE
style.l_list.selected_hover_background = HOVER
style.l_list.selected_background = REVERSE_IDLE
style.l_list.selected_hover_background = REVERSE_HOVER
style.l_list_text = Style(style.l_default)
style.l_list_text.idle_color = IDLE
style.l_list_text.hover_color = HOVER
style.l_list_text.selected_idle_color = WHITE
style.l_list_text.selected_hover_color = WHITE
style.l_list_text.selected_idle_color = REVERSE_TEXT
style.l_list_text.selected_hover_color = REVERSE_TEXT
style.l_list_text.insensitive_color = DISABLED
style.l_list2 = Style(style.l_list)
@@ -162,7 +198,7 @@ init python:
Image("vscrollbar_center.png", xalign=0.5, yalign=0.5),
xmaximum = SCROLLBAR_SIZE)
style.l_vscrollbar.hover_thumb = Fixed(
Solid(HOVER, xmaximum=8, xalign=0.5),
Solid(SCROLLBAR_HOVER, xmaximum=8, xalign=0.5),
Image("vscrollbar_center.png", xalign=0.5, yalign=0.5),
xmaximum = SCROLLBAR_SIZE)
style.l_vscrollbar.xmaximum = SCROLLBAR_SIZE
@@ -180,11 +216,13 @@ init python:
style.l_info_frame = Style(style.l_default)
style.l_info_frame.ypadding = 21
style.l_info_frame.xfill = True
style.l_info_frame.background = Fixed(
"#f9f9f9",
Frame("pattern.png", 0, 0, tile=True, ymaximum=5, yalign=0.0, yoffset=8),
Frame("pattern.png", 0, 0, tile=True, ymaximum=5, yalign=1.0, yoffset=-8),
INFO_WINDOW,
Frame(PATTERN, 0, 0, tile=True, ymaximum=5, yalign=0.0, yoffset=8),
Frame(PATTERN, 0, 0, tile=True, ymaximum=5, yalign=1.0, yoffset=-8),
)
style.l_info_frame.yminimum = 180
style.l_info_frame.ypos = 100
@@ -220,12 +258,19 @@ init python:
# Check boxes
style.l_checkbox = Style(style.l_button)
style.l_checkbox.left_padding = INDENT
style.l_checkbox.background = Image("checkbox_idle.png", yalign=0.5)
style.l_checkbox.hover_background = Image("checkbox_hover.png", yalign=0.5)
style.l_checkbox.selected_idle_background = Image("checkbox_selected_idle.png", yalign=0.5)
style.l_checkbox.selected_hover_background = Image("checkbox_selected_hover.png", yalign=0.5)
style.l_checkbox.insensitive_background = Image("checkbox_insensitive.png", yalign=0.5)
def checkbox(full, color):
if full:
return im.Twocolor("checkbox_full.png", color, color, yalign=0.5)
else:
return im.Twocolor("checkbox_empty.png", color, color, yalign=0.5)
style.l_checkbox.background = checkbox(False, IDLE)
style.l_checkbox.hover_background = checkbox(False, HOVER)
style.l_checkbox.selected_idle_background = checkbox(True, IDLE)
style.l_checkbox.selected_hover_background = checkbox(True, HOVER)
style.l_checkbox.insensitive_background = checkbox(False, DISABLED)
style.l_checkbox_text = Style(style.l_button_text)
style.l_checkbox_text.selected_font = LIGHT
@@ -237,12 +282,14 @@ init python:
# A progress bar and its frame.
style.l_progress_frame = Style(style.l_default)
style.l_progress_frame.background = Frame("pattern.png", 0, 0, tile=True)
style.l_progress_frame.background = Frame(PATTERN, 0, 0, tile=True)
style.l_progress_frame.ypadding = 5
style.l_progress_bar = Style(style.l_default)
style.l_progress_bar.left_bar = LIGHT_IDLE
style.l_progress_bar.left_bar = REVERSE_IDLE
style.l_progress_bar.right_bar = Null()
style.l_progress_bar.ymaximum = 24
# The projects window.
style.l_projects = Style(style.l_default)
style.l_projects.background = PROJECTS_WINDOW
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
# See LICENSE.txt for license details.
# This is used on Linux and Mac to prompt the user for the projects
# directory.
import sys
# Python3 and Python2-style imports.
try:
from tkinter import Tk
from tkinter.filedialog import askdirectory
except ImportError:
from Tkinter import Tk
from tkFileDialog import askdirectory
# Create the TK canvas.
if __name__ == "__main__":
root = Tk()
root.withdraw()
result = askdirectory(initialdir=sys.argv[1], parent=root, title="Select Ren'Py Projects Directory")
sys.stdout.write(result)
+34
View File
@@ -0,0 +1,34 @@
init python:
if persistent.translate_language is None:
persistent.translate_language = "english"
label translate:
python:
language = interface.input(_("Create or Update Translations"), _("Please enter the name of the language for which you want to create or update translations."), filename=True, default=persistent.translate_language, cancel=Jump("front_page"))
language = language.strip()
if not language:
interface.error(_("The language name can not be the empty string."))
persistent.translate_language = language
args = [ "translate", language ]
if language == "rot13":
args.append("--rot13")
else:
args.append("--empty")
interface.processing(_("Ren'Py is generating translations...."))
project.current.launch(args, wait=True)
project.current.update_dump(force=True)
interface.info(_("Ren'Py has finished generating [language] translations."))
jump front_page
-1
View File
@@ -55,7 +55,6 @@ screen update_channel:
add HALF_SPACER
hbox:
frame:
style "l_indent"
Binary file not shown.
Binary file not shown.
-144
View File
@@ -1,144 +0,0 @@
import binascii
def a2b(a):
return binascii.a2b_hex(''.join(a.split()))
resources = {
260 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c0 80
02 00 00 00 00 00 b8 00 29 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 01 00 01 50 7f 00 14 00
32 00 0e 00 01 00 00 00 ff ff 80 00 4f 00 4b 00
00 00 00 00 00 00 00 00 00 00 00 00 04 08 00 50
07 00 07 00 aa 00 0c 00 ea 03 00 00 ff ff 81 00
00 00 00 00
'''),
261 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c0 80
04 00 00 00 00 00 b8 00 3a 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 80 00 81 50 07 00 14 00
aa 00 0c 00 ec 03 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 01 00 01 50 7f 00 25 00
32 00 0e 00 01 00 00 00 ff ff 80 00 4f 00 4b 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 50
4a 00 25 00 32 00 0e 00 02 00 00 00 ff ff 80 00
43 00 61 00 6e 00 63 00 65 00 6c 00 00 00 00 00
00 00 00 00 00 00 00 00 04 08 00 50 07 00 07 00
aa 00 0c 00 eb 03 00 00 ff ff 81 00 00 00 00 00
'''),
262 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c0 80
04 00 00 00 00 00 b8 00 29 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 00 00 01 50 7f 00 14 00
32 00 0e 00 06 00 00 00 ff ff 80 00 59 00 65 00
73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
04 08 00 50 07 00 07 00 aa 00 0c 00 ed 03 00 00
ff ff 81 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 01 50 07 00 14 00 32 00 0e 00 07 00 00 00
ff ff 80 00 4e 00 6f 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 01 50 4b 00 14 00 32 00 0e 00
02 00 00 00 ff ff 80 00 43 00 61 00 6e 00 63 00
65 00 6c 00 00 00 00 00
'''),
263 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 80 00 ca 80
03 00 00 00 00 00 e2 00 29 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 81 50 07 00 14 00
97 00 0e 00 eb 03 00 00 6d 00 73 00 63 00 74 00
6c 00 73 00 5f 00 70 00 72 00 6f 00 67 00 72 00
65 00 73 00 73 00 33 00 32 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 04 08 00 50 07 00 07 00
d4 00 0c 00 ea 03 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 50 a9 00 14 00
32 00 0e 00 02 00 00 00 ff ff 80 00 43 00 61 00
6e 00 63 00 65 00 6c 00 00 00 00 00
'''),
264 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c0 80
04 00 00 00 00 00 b8 00 3a 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 a0 00 81 50 07 00 14 00
aa 00 0c 00 ec 03 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 01 00 01 50 7f 00 25 00
32 00 0e 00 01 00 00 00 ff ff 80 00 4f 00 4b 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 50
4a 00 25 00 32 00 0e 00 02 00 00 00 ff ff 80 00
43 00 61 00 6e 00 63 00 65 00 6c 00 00 00 00 00
00 00 00 00 00 00 00 00 04 08 00 50 07 00 07 00
aa 00 0c 00 eb 03 00 00 ff ff 81 00 00 00 00 00
'''),
265 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c8 80
12 00 00 00 00 00 d9 00 fc 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 01 00 01 50 6c 00 ea 00
30 00 0e 00 01 00 00 00 ff ff 80 00 4f 00 4b 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 50
a2 00 ea 00 30 00 0e 00 02 00 00 00 ff ff 80 00
43 00 61 00 6e 00 63 00 65 00 6c 00 00 00 00 00
00 00 00 00 00 00 00 00 07 00 00 50 06 00 06 00
cc 00 4e 00 ff ff ff ff ff ff 80 00 00 00 00 00
00 00 00 00 00 00 00 00 03 00 21 50 48 00 12 00
84 00 64 00 03 00 00 00 ff ff 85 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 02 50 0c 00 24 00
c0 00 08 00 04 00 00 00 ff ff 82 00 00 00 00 00
00 00 00 00 00 00 00 00 80 00 81 50 0c 00 30 00
c0 00 0e 00 05 00 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 01 50 9c 00 42 00
30 00 0e 00 06 00 00 00 ff ff 80 00 41 00 64 00
64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
07 00 00 50 06 00 54 00 cb 00 3c 00 ff ff ff ff
ff ff 80 00 00 00 00 00 00 00 00 00 00 00 00 00
03 00 21 50 48 00 61 00 84 00 64 00 07 00 00 00
ff ff 85 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 02 50 0c 00 72 00 c0 00 08 00 08 00 00 00
ff ff 82 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 01 50 9c 00 7e 00 30 00 0e 00 09 00 00 00
ff ff 80 00 41 00 64 00 64 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 01 50 06 00 96 00
42 00 0e 00 0a 00 00 00 ff ff 80 00 41 00 64 00
64 00 20 00 66 00 69 00 6c 00 65 00 2e 00 2e 00
2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 01 50 4e 00 96 00 42 00 0e 00 0b 00 00 00
ff ff 80 00 41 00 64 00 64 00 20 00 6e 00 65 00
77 00 20 00 66 00 69 00 6c 00 65 00 2e 00 2e 00
2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 01 50 06 00 a8 00 42 00 0e 00 0c 00 00 00
ff ff 80 00 41 00 64 00 64 00 20 00 66 00 6f 00
6c 00 64 00 65 00 72 00 2e 00 2e 00 2e 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 50
06 00 c6 00 cc 00 1e 00 ff ff ff ff ff ff 80 00
43 00 6f 00 6d 00 6d 00 61 00 6e 00 64 00 20 00
6c 00 69 00 6e 00 65 00 3a 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 80 00 81 50 0c 00 d2 00
c0 00 0e 00 0e 00 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 02 50 0c 00 14 00
3c 00 08 00 1e 00 00 00 ff ff 82 00 4f 00 70 00
74 00 69 00 6f 00 6e 00 3a 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 02 50 0c 00 63 00
3c 00 08 00 46 00 00 00 ff ff 82 00 43 00 6f 00
6d 00 6d 00 61 00 6e 00 64 00 3a 00 00 00 00 00
'''),
270 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 48 04 00 44
02 00 00 00 00 00 23 01 1a 00 00 00 00 00 00 00
08 00 00 00 00 00 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 01 00 02 50 00 00 07 00
23 01 08 00 ff ff ff ff ff ff 82 00 53 00 74 00
61 00 74 00 69 00 63 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 02 40 00 00 12 00 23 01 08 00
5f 04 00 00 ff ff 82 00 73 00 74 00 63 00 33 00
32 00 00 00 00 00
'''),
}
File diff suppressed because it is too large Load Diff
-177
View File
@@ -1,177 +0,0 @@
init python:
import renpy.tools.archiver as archiver
import os
import os.path
import fnmatch
label archiver:
python hide:
# Get the options
name = project.info.get('archive_name', "data")
include = project.info.get('archive_include', "*.png *.gif *.jpg")
exclude = project.info.get('archive_exclude', "presplash.png")
# Allow the user to set the options.
while True:
set_tooltip("")
screen()
ui.vbox()
title(_(u"Archiver"))
text(_(u"The archiver allows you to obfuscate your game by including files in an archive file."))
ui.null(height=15)
text_variable(_(u"Archive Name:"), name, "name",
_(u"The name of the archive to create."))
text_variable(_(u"Include Patterns:"), include, "include",
_(u"Files matching these patterns are included in the archive."))
text_variable(_(u"Exclude Patterns:"), exclude, "exclude",
_(u"Files matching these patterns are excluded from the archive."))
ui.null(height=15)
button(_(u"Archive"), ui.returns("archive"), _(u"Build the archive."))
button(_(u"Cancel"), ui.jumps("top"), "")
ui.close()
act = interact()
if act == "name":
name = input(
_(u"Archive Name"),
_(u"The name of the archive file to create, without the .rpa extension.\n\nThe \"data\" archive is loaded automatically. Other archives must be added to config.archives."),
name)
elif act == "include":
include = input(
_(u"Include Patterns"),
_(u"This is a space-separated list of file patterns. Files matching these patterns are added to the archive.\n\nAsterisks (*) can be used as a wildcard."),
include)
elif act == "exclude":
exclude = input(
_(u"Include Patterns"),
_(u"This is a space-separated list of file patterns. Files matching these patterns are excluded from the archive. If a file is matched by both an exclude and include pattern, the exclude takes precedence.\n\nAsterisks (*) can be used as a wildcard."),
exclude)
elif act == "archive":
break
# Store the options.
project.info["archive_name"] = name
project.info["archive_include"] = include
project.info["archive_exclude"] = exclude
project.save()
# Break up the extension lists.
include = [ i.strip() for i in include.split() ]
exclude = [ i.strip() for i in exclude.split() ]
# Get the gamedir and the archived dir.
gamedir = os.path.join(project.path, "game")
archived = os.path.join(project.path, "archived")
# The prefix of the archive file.
prefix = os.path.join(gamedir, name)
archived_files = set()
files = [ ]
# Choose files to archive.
set_tooltip("")
info(
_(u"Scanning Files..."),
"")
for bdir in (gamedir, archived):
for dirname, dirs, filenames in os.walk(bdir):
dirs[:] = [ i for i in dirs if not i[0] == '.' ]
for fn in filenames:
fullfn = dirname + "/" + fn
shortfn = fullfn[len(bdir)+1:]
if fn[0] == ".":
continue
if shortfn in archived_files:
continue
should_archive = False
for i in include:
if fnmatch.fnmatch(fn, i):
should_archive = True
for i in exclude:
if fnmatch.fnmatch(fn, i):
should_archive = False
if not should_archive:
continue
files.append((fullfn, shortfn))
archived_files.add(shortfn)
if not files:
error(_(u"The patterns did not match any files, so no archive was created."))
# Actually archiving files.
info(_(u"Archiving Files..."), "")
archiver.archive(prefix, files)
# Move files out of the way.
for fullfn, shortfn in files:
afn = archived + "/" + shortfn
if fullfn == afn:
continue
try:
os.makedirs(os.path.dirname(afn))
except:
pass
try:
os.rename(fullfn, afn)
except:
os.rename(afn, afn + ".old")
os.rename(fullfn, afn)
os.unlink(afn + ".old")
# Report success.
screen()
ui.vbox()
title(_(u"Success"))
text(_(u"The files have been added to the archive, and moved into the \"archived\" directory. Future runs of the archiver will archive files in both the \"game\" and \"archived\" directories."))
ui.null(height=20)
button(_(u"Return"), ui.jumps("top"), None)
ui.close()
interact()
-329
View File
@@ -1,329 +0,0 @@
# http://www.csn.ul.ie/~caolan/publink/winresdump/winresdump/doc/pefile.html
# Contains a reasonable description of the format.
import struct
import sys
import array
import pefile
# This class performs various operations on memory-loaded binary files,
# including modifications.
class BinFile(object):
def set_u32(self, addr, value):
self.a[addr+0] = (value >> 0) & 0xff
self.a[addr+1] = (value >> 8) & 0xff
self.a[addr+2] = (value >> 16) & 0xff
self.a[addr+3] = (value >> 24) & 0xff
def u32(self):
addr = self.addr
rv = self.a[addr]
rv |= self.a[addr+1] << 8
rv |= self.a[addr+2] << 16
rv |= self.a[addr+3] << 24
self.addr += 4
return rv
def u16(self):
addr = self.addr
rv = self.a[addr]
rv |= self.a[addr+1] << 8
self.addr += 2
return rv
def u8(self):
rv = self.a[self.addr]
self.addr += 1
return rv
def name(self):
c = self.u16()
rv = u""
for _i in range(c):
rv += unichr(self.u16())
return rv
def seek(self, addr):
self.addr = addr
def tostring(self):
return self.a.tostring()
def substring(self, start, len): #@ReservedAssignment
return self.a[start:start+len].tostring()
def __init__(self, data):
self.a = array.array('B')
self.a.fromstring(data)
##############################################################################
# These functions parse data out of the file. In these functions, offset is
# relative to the start of the file.
# The virtual address of the resource segment.
resource_virtual = 0
# This parses a data block out of the resources.
def parse_data(bf, offset):
bf.seek(offset)
data_offset = bf.u32()
data_len = bf.u32()
code_page = bf.u32()
bf.u32()
l = [ ]
bf.seek(data_offset - resource_virtual)
for _i in range(data_len):
l.append(chr(bf.u8()))
return (code_page, "".join(l))
# This parses a resource directory.
def parse_directory(bf, offset):
bf.seek(offset)
char = bf.u32() #@UnusedVariable
timedate = bf.u32() #@UnusedVariable
major = bf.u16() #@UnusedVariable
minor = bf.u16() #@UnusedVariable
n_named = bf.u16()
n_id = bf.u16()
entries = [ ]
for _i in range(n_named + n_id):
entries.append((bf.u32(), bf.u32()))
rv = { }
for name, value in entries:
if name & 0x80000000:
bf.seek((name & 0x7fffffff))
name = bf.name()
if value & 0x80000000:
value = parse_directory(bf, value & 0x7fffffff)
else:
value = parse_data(bf, value)
rv[name] = value
return rv
##############################################################################
# This utility function displays the tree of resources that have been loaded.
def show_resources(d, prefix):
if not isinstance(d, dict):
print prefix, "Codepage", d[0], "length", len(d[1])
return
for k in d:
print prefix, k
show_resources(d[k], prefix + " ")
##############################################################################
# These functions repack the resources into a new resource segment. Here,
# the offset is relative to the start of the resource segment.
class Packer(object):
def pack(self, d):
self.data = ""
self.data_offset = 0
self.entries = ""
self.entries_offset = 0
head = self.pack_dict(d, 0)
self.data = ""
self.data_offset = len(head) + len(self.entries)
self.entries = ""
self.entries_offset = len(head)
return self.pack_dict(d, 0) + self.entries + self.data
def pack_name(self, s):
rv = self.data_offset + len(self.data)
l = len(s)
s = s.encode("utf-16le")
self.data += struct.pack("<H", l) + s + "\0\0"
return rv
def pack_tuple(self, t):
codepage, data = t
rv = len(self.entries) + self.entries_offset
if len(self.data) % 2:
self.data += "P"
daddr = len(self.data) + self.data_offset
self.entries += struct.pack("<IIII", daddr + resource_virtual, len(data), codepage, 0)
self.data += data
# if len(self.data) % 1 == 1:
# self.data += 'P'
return rv
def pack_dict(self, d, offset):
name_entries = sorted((a, b) for a, b in d.iteritems() if isinstance(a, unicode))
id_entries = sorted((a, b) for a, b in d.iteritems() if isinstance(a, int))
rv = struct.pack("<IIHHHH", 0, 0, 4, 0, len(name_entries), len(id_entries))
offset += len(rv) + (len(name_entries) + len(id_entries)) * 8
rest = ""
for (name, value) in name_entries + id_entries:
if isinstance(name, unicode):
name = 0x80000000 | self.pack_name(name)
if isinstance(value, dict):
addr = offset | 0x80000000
packed = self.pack_dict(value, offset)
offset += len(packed)
rest += packed
else:
addr = self.pack_tuple(value)
rv += struct.pack("<II", name, addr)
return rv + rest
##############################################################################
# This loads in an icon file, and returns a dictionary that is suitable for
# use in the resources of an exe file.
def load_icon(fn):
f = BinFile(file(fn, "rb").read())
f.seek(0)
f.u16()
f.u16()
count = f.u16()
rv = { }
rv[3] = { }
group = struct.pack("HHH", 0, 1, count)
for i in range(count):
width = f.u8()
height = f.u8()
colors = f.u8()
reserved = f.u8()
planes = f.u16()
bpp = f.u16()
size = f.u32()
offset = f.u32()
addr = f.addr
f.seek(offset + 16)
if not f.u32():
f.set_u32(offset + 20, 0)
rv[3][i + 1] = { 0 : (1252, f.substring(offset, size)) }
group += struct.pack("BBBBHHIH", width, height, colors, reserved,
planes, bpp, size, i + 1)
f.seek(addr)
rv[14] = { 1 : { 0 : (1252, group) } }
return rv
##############################################################################
# This is the main function that should be called externally, that copies over
# the icons.
def change_icons(oldexe, icofn):
global resource_virtual
pe = pefile.PE(oldexe)
for s in pe.sections:
if s.Name == ".rsrc\0\0\0":
rsrc_section = s
break
else:
raise Exception("Couldn't find resource section.")
base = rsrc_section.PointerToRawData
resource_virtual = rsrc_section.VirtualAddress
physize = rsrc_section.SizeOfRawData
virsize = rsrc_section.Misc_VirtualSize
f = file(oldexe, "rb")
f.seek(base)
data = f.read(physize)
f.close()
bf = BinFile(data)
resources = parse_directory(bf, 0)
# show_resources(resources, "")
resources.update(load_icon(icofn))
# show_resources(resources, "")
rsrc = Packer().pack(resources)
alignment = pe.OPTIONAL_HEADER.SectionAlignment
# print "Alignment is", alignment
if len(rsrc) % alignment:
pad = alignment - (len(rsrc) % alignment)
padding = "RENPYVNE" * (pad / 8 + 1)
padding = padding[:pad]
rsrc += padding
newsize = len(rsrc)
rsrc_section.Misc_VirtualSize += newsize - virsize
rsrc_section.Misc_PhysicalAddress += newsize - virsize
rsrc_section.Misc += newsize - virsize
rsrc_section.SizeOfRawData += newsize - physize
pe.OPTIONAL_HEADER.SizeOfInitializedData += newsize - physize
# Resource size.
pe.OPTIONAL_HEADER.DATA_DIRECTORY[2].Size += newsize - virsize
# Compute the total size of the image.
total_size = 0
for i in pe.sections:
sec_size = i.Misc_VirtualSize
sec_size = sec_size - (sec_size % alignment) + alignment
print sec_size
total_size += sec_size
pe.OPTIONAL_HEADER.SizeOfImage = total_size
return pe.write()[:base] + rsrc
if __name__ == "__main__":
f = file(sys.argv[3], "wb")
f.write(change_icons(sys.argv[1], sys.argv[2]))
f.close()
-324
View File
@@ -1,324 +0,0 @@
init python:
import codecs
import re
import sys
##########################################################################
# Code to update options.rpy
def list_logical_lines(filename):
"""
This reads in filename, and turns it into a list of logical
lines.
"""
f = codecs.open(filename, "rb", "utf-8")
data = f.read()
f.close()
# The result.
rv = []
# The current position we're looking at in the buffer.
pos = 0
# Looping over the lines in the file.
while pos < len(data):
# The line that we're building up.
line = ""
# The number of open parenthesis there are right now.
parendepth = 0
# Looping over the characters in a single logical line.
while pos < len(data):
c = data[pos]
if c == '\n' and not parendepth:
rv.append(line)
pos += 1
# This helps out error checking.
line = ""
break
# Backslash/newline.
if c == "\\" and data[pos+1] == "\n":
pos += 2
line += "\\\n"
continue
# Parenthesis.
if c in ('(', '[', '{'):
parendepth += 1
if c in ('}', ']', ')') and parendepth:
parendepth -= 1
# Comments.
if c == '#':
while data[pos] != '\n':
line += data[pos]
pos += 1
continue
# Strings.
if c in ('"', "'", "`"):
delim = c
line += c
pos += 1
escape = False
while pos < len(data):
c = data[pos]
if escape:
escape = False
pos += 1
line += c
continue
if c == delim:
pos += 1
line += c
break
if c == '\\':
escape = True
line += c
pos += 1
continue
continue
line += c
pos += 1
if line:
rv.append(line)
return rv
def switch_theme(theme_function, name):
"""
Switches the theme of the current project to the named theme.
"""
theme_functions = set(i[1] for i in themes)
td = theme_data[name].copy()
td["name"] = name
# Did we change the file at all?
changed = False
filename = os.path.join(project.gamedir, "options.rpy")
out = codecs.open(filename + ".new", "wb", "utf-8")
for l in list_logical_lines(filename):
m = re.match(r' theme.(\w+)\(', l)
if m:
if m.group(1) in theme_functions:
l = theme_templates[theme_function] % td
changed = True
out.write(l + "\n")
out.close()
if changed:
try:
os.unlink(filename + ".bak")
except:
pass
os.rename(filename, filename + ".bak")
os.rename(filename + ".new", filename)
else:
os.unlink(filename + ".new")
error(_(u"Could not modify options.rpy. Perhaps it was changed too much."))
set_tooltip(_(u"Theme changed to %s.") % name)
renpy.jump("top")
curried_switch_theme = renpy.curry(switch_theme)
##########################################################################
# Code that handles display.
current_theme_function = None
current_theme = None
def show_theme(function, name, target):
"""
Changes from the current theme to the theme named `name`.
"""
if current_theme_function == function and current_theme == name:
return
store.current_theme_function = function
store.current_theme = name
td = theme_data[name].copy()
td["rounded_window"] = False
renpy.style.restore(style_backup)
getattr(theme, function)(**td)
customize_styles()
renpy.style.rebuild()
renpy.jump(target)
curried_show_theme = renpy.curry(show_theme)
def theme_demo():
# The sample area, that shows what the theme looks like.
ui.window(style='default', background="#444", xpadding=1, ypadding=1)
ui.window(style='gm_root', xpadding=5, ypadding=5)
ui.vbox(5)
# Display Preference.
ui.window(style=style.prefs_pref_frame)
ui.vbox(style=style.prefs_pref_box)
ui.hbox(style=style.prefs_pref_choicebox)
layout.label(_(u"Display"), "prefs")
layout.button(_(u"Window"), "prefs", clicked=does_nothing, selected=True)
layout.button(_(u"Fullscreen"), "prefs", clicked=does_nothing, selected=False)
layout.button(_(u"Planetarium"), "prefs", clicked=None, selected=False)
ui.close()
ui.close()
# Volume Preference
ui.window(style=style.prefs_pref_frame)
ui.vbox(style=style.prefs_pref_box)
layout.label(_(u"Music Volume"), "prefs")
ui.vbox(style=style.prefs_volume_box)
ui.bar(128,
92,
changed=does_nothing,
style=style.prefs_volume_slider)
layout.button(_(u"Test"), "soundtest", clicked=does_nothing)
ui.close()
ui.close()
ui.close() # vbox
# Used to have buttons not do anything.
def does_nothing(*args):
return
label choose_theme:
python:
if not os.path.exists(os.path.join(project.gamedir, "options.rpy")):
error(_(u"The options.rpy file does not exist in the game directory, so this launcher cannot change the theme."))
current_theme = None
theme_adjustment = ui.adjustment()
label repeat_choose_theme:
python hide:
tip = _(u"Themes control the basic look of interface elements. You'll be able to pick a color scheme next.")
screen()
ui.vbox()
title(_(u"Choose Theme"))
ui.grid(2, 1, padding=10, xfill=True)
# The scroll area, that lets the user pick a theme.
scrolled('top', theme_adjustment)
ui.vbox()
for name, function, exemplar in themes:
button(name,
ui.returns(function),
"",
hovered=curried_show_theme(function, exemplar, "repeat_choose_theme"),
unhovered=does_nothing)
ui.close() # vbox
ui.close() # scrolled
theme_demo()
ui.close() # grid
ui.close() # vbox
set_tooltip(tip)
store.theme_function = interact()
label choose_color_scheme:
python:
current_theme = None
theme_adjustment = ui.adjustment()
label repeat_choose_color_scheme:
python hide:
tip = _(u"Please choose a color scheme for your project.")
themes = theme_data.keys()
themes.sort()
screen()
ui.vbox()
title(_(u"Choose Color Scheme"))
ui.grid(2, 1, padding=10, xfill=True)
# The scroll area, that lets the user pick a theme.
scrolled('choose_theme', theme_adjustment)
ui.vbox()
for i in themes:
button(i,
curried_switch_theme(theme_function, i),
"",
hovered=curried_show_theme(theme_function, i, "repeat_choose_color_scheme"),
unhovered=does_nothing)
ui.close() # vbox
ui.close() # scrolled
theme_demo()
ui.close() # grid
ui.close() # vbox
set_tooltip(tip)
interact()
-536
View File
@@ -1,536 +0,0 @@
import os.path
import zipfile
import tarfile
import time
import sys
import struct
import zlib
zlib.Z_DEFAULT_COMPRESSION = 9
import binascii
import change_icon
import argparse
# The directory containing the project that we'll be distributing.
project_path = None
# The path to the Ren'Py base directory.
renpy_base = None
# A function that is called to give information about what is going
# on now.
def info(title, body):
return
# A function that is called to give a progress report.
def progress(title, total, amount):
amount += 1
if amount % 25 == 0:
print title, amount, "of", total
# A translation function.
def _(s):
return s
# These are files that are ignored wherever they are found in a
# distribution.
ignored_files = (
"thumbs.db",
"traceback.txt",
"errors.txt",
"files.txt",
"saves"
)
# These are files (and directories) that are ignored when found in
# the root directory of the distribution.
root_ignored_files = (
"common",
"renpy",
"renpy.code",
"python23.dll",
"python24.dll",
"python25.dll",
"msvcr71.dll",
"lib",
"iliad-icon.png",
"manifest.xml",
"icon.ico",
"icon.icns",
"launcherinfo.py",
"archived",
)
# Extensions that should be made executable.
executable_extensions = (
"MacOS",
"so",
"dylib",
".sh",
"python",
"python.real",
)
class MyZipFile(zipfile.ZipFile):
"""
Modified ZipFile class that can insert a file into the archive,
using a supplied ZipInfo object. Code comes from the writestr
and write methods of ZipFile.
"""
def write_file_with_zipinfo(self, filename, zinfo, compress_type=None):
"""Put the bytes from filename into the archive under the name
arcname."""
st = os.stat(filename)
if compress_type is None:
zinfo.compress_type = self.compression
else:
zinfo.compress_type = compress_type
zinfo.file_size = st.st_size
zinfo.flag_bits = 0x00
zinfo.header_offset = self.fp.tell() # Start of header bytes
self._writecheck(zinfo)
self._didModify = True
fp = open(filename, "rb")
# Must overwrite CRC and sizes with correct data later
zinfo.CRC = CRC = 0
zinfo.compress_size = compress_size = 0
zinfo.file_size = file_size = 0
self.fp.write(zinfo.FileHeader())
if zinfo.compress_type == zipfile.ZIP_DEFLATED:
cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
zlib.DEFLATED, -15)
else:
cmpr = None
while 1:
buf = fp.read(1024 * 64)
if not buf:
break
file_size = file_size + len(buf)
CRC = binascii.crc32(buf, CRC)
if cmpr:
buf = cmpr.compress(buf)
compress_size = compress_size + len(buf)
self.fp.write(buf)
fp.close()
if cmpr:
buf = cmpr.flush()
compress_size = compress_size + len(buf)
self.fp.write(buf)
zinfo.compress_size = compress_size
else:
zinfo.compress_size = file_size
zinfo.CRC = CRC
zinfo.file_size = file_size
# Seek backwards and write CRC and file sizes
position = self.fp.tell() # Preserve current position in file
self.fp.seek(zinfo.header_offset + 14, 0)
self.fp.write(struct.pack("<lLL", zinfo.CRC, zinfo.compress_size,
zinfo.file_size))
self.fp.seek(position, 0)
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
def tree(
src,
dest,
exclude_suffix=[ ".pyc", "~", ".bak", ".old", ".new" ],
exclude_prefix=[ "#", "." ],
exclude_files = set(ignored_files),
root_exclude_prefix = [ ],
root_exclude_suffix = [ ".py", ".sh", ".app" ],
root_exclude_files = set(root_ignored_files),
root=False):
"""
Returns a list of source-filename, destination-filename pairs.
"""
if dest[0] != "/":
raise Exception("Destination must begin with /: %r" % dest)
src = src.rstrip('/')
dest = dest.rstrip('/')
def include(fn, is_root):
"""
Returns True if the file should be included in the list of
files we are copying.
"""
for i in exclude_suffix:
if fn.endswith(i):
return False
for i in exclude_prefix:
if fn.startswith(i):
return False
if fn in exclude_files:
return False
if not root or not is_root:
return True
for i in root_exclude_suffix:
if fn.endswith(i):
return False
for i in root_exclude_prefix:
if fn.startswith(i):
return False
if fn in root_exclude_files:
return False
return True
rv = [ ]
# Walk the tree, including what is necessary.
for srcdir, dirs, files in os.walk(src):
is_root = (srcdir == src)
srcdir += "/"
destdir = dest + srcdir[len(src):]
destdir.replace("\\", "/")
rv.append((srcdir, destdir))
for fn in files:
if not include(fn, is_root):
continue
sfn = srcdir + fn
dfn = destdir + fn
rv.append((sfn, dfn))
dirs[:] = [ i for i in dirs if include(i, is_root) ]
rv = [ (a.replace("\\", "/"), b.replace("\\", "/")) for a, b in rv ]
return rv
def make_zip(t, filename, files, file_data):
"""
This creates `filename`.zip, containing `files`, placed in the
`filename` directory. `file_data` is a map from source file to
replacement data.
"""
files.sort(key=lambda a : a[1])
progress_len = len(files)
fn = os.path.join(os.path.dirname(project_path), filename)
zf = MyZipFile(fn + ".zip", "w", zipfile.ZIP_DEFLATED)
for i, (fn, an) in enumerate(files):
progress(t, progress_len, i)
if os.path.isdir(fn):
continue
zi = zipfile.ZipInfo(filename + an)
s = os.stat(fn)
zi.date_time = time.gmtime(s.st_mtime)[:6]
zi.compress_type = zipfile.ZIP_DEFLATED
zi.create_system = 3
for i in executable_extensions:
if os.path.dirname(fn).endswith(i) or fn.endswith(i):
zi.external_attr = long(0100777) << 16
break
else:
zi.external_attr = long(0100666) << 16
if fn in file_data:
data = file_data[fn]
zf.writestr(zi, data)
else:
zf.write_file_with_zipinfo(fn, zi)
zf.close()
def make_tar(t, filename, files):
"""
Makes a tarfile, as above.
"""
files.sort(key=lambda a : a[1])
progress_len = len(files)
fn = os.path.join(os.path.dirname(project_path), filename)
tf = tarfile.open(fn + ".tar.bz2", "w:bz2")
tf.dereference = True
for j, (fn, an) in enumerate(files):
progress(t, progress_len, j)
info = tf.gettarinfo(fn, filename + an)
perms = 0666
if info.isdir():
perms = 0777
for i in executable_extensions:
if fn.endswith(i):
perms = 0777
info.mode = perms
info.uid = 1000
info.gid = 1000
info.uname = "renpy"
info.gname = "renpy"
if info.isreg():
tf.addfile(info, file(fn, "rb"))
else:
tf.addfile(info)
tf.close()
def distribute(
_project_path,
_renpy_base,
base_name,
executable_name,
ignore_extensions,
documentation_extensions,
build_windows,
build_linux,
build_mac,
build_all):
global project_path
global renpy_base
project_path = _project_path
renpy_base = _renpy_base
base_name = base_name.encode("utf-8")
executable_name = executable_name.encode("utf-8")
ignore_extensions = ignore_extensions.encode("utf-8")
documentation_extensions = documentation_extensions.encode("utf-8")
# Convert some of these to more useful formats.
ignore_extensions = [ i.strip() for i in ignore_extensions.split() ]
documentation_extensions = [ i.strip() for i in documentation_extensions.split() ]
# Scan for the files we want to include in the various distributions.
info(_(u"Scanning..."), "")
# Files included in the various distributions.
multi_files = [ ]
win_files = [ ]
linux_files = [ ]
mac_files = [ ]
# A map from source file name to replacement data to be placed in
# that file.
file_data = { }
######################################################################
# Multi files.
rb = renpy_base.replace("\\", "/") + "/"
# Project files.
multi_files.extend(tree(project_path, "/", root=True, exclude_suffix=ignore_extensions))
multi_files.append((rb + "renpy.py", "/" + executable_name + ".py"))
# Renpy files.
multi_files.extend(tree(rb + "common", "/common"))
multi_files.extend(tree(rb + "renpy", "/renpy"))
multi_files.append((rb + "LICENSE.txt", "/renpy/LICENSE.txt"))
def add_script_version(fn, ignore_extensions=ignore_extensions, multi_files=multi_files, rb=rb):
"""
Add a script_version file if it does not already exist, and if the
extension is allowed by the game.
"""
for _a, b in multi_files:
if b == "/game/" + fn:
return
for i in ignore_extensions:
if fn.endswith(i):
return
multi_files.append((rb + "launcher/" + fn, "/game/" + fn))
add_script_version("script_version.rpy")
add_script_version("script_version.rpyc")
######################################################################
# Windows files.
if build_windows or build_all:
win_files.append((rb + "renpy.exe", "/" + executable_name + ".exe"))
win_files.append((rb + "python26.dll", "/python26.dll"))
win_files.append((rb + "msvcr90.dll", "/msvcr90.dll"))
win_files.append((rb + "Microsoft.VC90.CRT.manifest", "/Microsoft.VC90.CRT.manifest"))
win_files.append((rb + "lib", "/lib"))
win_files.append((rb + "lib/dxwebsetup.exe", "/lib/dxwebsetup.exe"))
win_files.extend(tree(rb + "lib/windows-x86", "/lib/windows-x86"))
if os.path.exists(project_path + "/icon.ico"):
file_data[rb + "renpy.exe"] = change_icon.change_icons(
rb + "renpy.exe",
project_path + "/icon.ico",
)
######################################################################
# Linux files.
if build_linux or build_all:
linux_files.append((rb + "renpy.sh", "/" + executable_name + ".sh"))
linux_files.append((rb + "lib", "/lib"))
linux_files.append((rb + "lib/python", "/lib/python"))
linux_files.extend(tree(rb + "lib/linux-x86", "/lib/linux-x86"))
# Warning: The tar.bz2 builder doesn't support file_data.
######################################################################
# Mac (non-app) files.
if build_mac or build_all:
mac_files = tree(rb + "renpy.app",
"/" + executable_name + ".app")
# Rename executable.
mac_files = [ (fn, an.replace("Ren'Py Launcher", executable_name)) for (fn, an) in mac_files ]
# Plist file.
quoted_name = executable_name.replace("&", "&amp;").replace("<", "&lt;")
info_plist = file(rb + "renpy.app/Contents/Info.plist", "rb").read().replace("Ren'Py Launcher", quoted_name)
file_data[rb + "renpy.app/Contents/Info.plist"] = info_plist
# Launcher script.
quoted_name = executable_name.replace("\"", "\\\"")
launcher_py = file(rb + "renpy.app/Contents/Resources/launcher.py", "rb").read().replace("Ren'Py Launcher", quoted_name)
file_data[rb + "renpy.app/Contents/Resources/launcher.py"] = launcher_py
# Icon file.
if os.path.exists(project_path + "/icon.icns"):
icon_data = file(project_path + "/icon.icns", "rb").read()
file_data[rb + "renpy.app/Contents/Resources/launcher.icns"] = icon_data
######################################################################
# Now, build the various distributions.
if build_windows:
make_zip(
_(u"Building Windows..."),
base_name + "-win32",
multi_files + win_files,
file_data)
if build_linux:
make_tar(
_(u"Building Linux..."),
base_name + "-linux-x86",
multi_files + linux_files)
if build_mac:
# Reorganize the files so all the non application files live inside
# the application. If there's documentation involved, then it
# lives in both places.
macapp_files = [ ]
for fn, an in multi_files + mac_files:
if not an.startswith("/" + executable_name + ".app"):
new_an = "/" + executable_name + ".app/Contents/Resources/autorun" + an
macapp_files.append((fn, new_an))
if an.rindex('/') == 0:
for i in documentation_extensions:
if fn.endswith(i):
macapp_files.append((fn, an))
break
else:
macapp_files.append((fn, an))
make_zip(
_(u"Building Macintosh..."),
base_name + "-mac",
macapp_files,
file_data)
if build_all:
make_zip(
_(u"Building Combined..."),
base_name + "-all",
multi_files + win_files + linux_files + mac_files,
file_data)
def main():
ap = argparse.ArgumentParser(description="Prepare a Ren'Py game for distribution.")
ap.add_argument("project_path", type=str, help="The path to the directory containing the project.")
ap.add_argument("base_name", type=str, help="The base name of the archive files produced.")
ap.add_argument("executable_name", type=str, help="The name of the executables produced.")
ap.add_argument("--renpy-base", type=str, default=None, help="The path to the Ren'Py distribution.")
ap.add_argument("--ignore-extensions", type=str, default="~ .bak", help="A space-separated list of extensions to ignore.")
ap.add_argument("--documentation-extensions", type=str, default="txt html", help="A space-separated list of extensions to treat as documentation.")
ap.add_argument("--build-windows", action="store_true", default=False, help="Build the Windows distribution.")
ap.add_argument("--build-mac", action="store_true", default=False, help="Build the Mac OS X distribution.")
ap.add_argument("--build-linux", action="store_true", default=False, help="Build the Windows distribution.")
ap.add_argument("--build-all", action="store_true", default=False, help="Build the All Platforms distribution.")
args = ap.parse_args()
renpy_base = args.renpy_base
if renpy_base is None:
renpy_base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
distribute(
args.project_path,
renpy_base,
base_name=args.base_name,
executable_name=args.executable_name,
ignore_extensions=args.ignore_extensions,
documentation_extensions=args.documentation_extensions,
build_windows=args.build_windows,
build_mac=args.build_mac,
build_linux=args.build_linux,
build_all=args.build_all)
if __name__ == "__main__":
main()
-185
View File
@@ -1,185 +0,0 @@
# This file contains the code needed to build a Ren'Py distribution.
init 12 python:
import distribute
distribute.progress = progress
distribute.info = info
distribute._ = _
def dist_exists(fn):
"""
Returns true if the given file exists in the renpy directory.
"""
return os.path.exists(os.path.join(config.renpy_base, fn))
label distribute:
call lint
if not yesno(_(u"Building Distributions"),
_(u"I've just performed a lint on your project. If it contains errors, you should say no and fix them.\nPlease also check {a=http://www.renpy.org/wiki/renpy/Download_Ren'Py}www.renpy.org{/a} to see if updates or fixes are available.\n\nDo you want to continue?")):
jump top
python hide:
# Do we have the files?
has_windows = dist_exists("renpy.exe")
has_linux = dist_exists("lib/linux-x86")
has_mac = dist_exists("renpy.app")
has_all = has_windows and has_mac and has_linux
# Should we build these distributions?
build_windows = has_windows and project.info.get("build_windows", has_windows)
build_linux = has_linux and project.info.get("build_linux", has_linux)
build_mac = has_mac and project.info.get("build_mac", has_mac)
build_all = has_all and project.info.get("build_all", False)
# The base name of the distribution.
base_name = project.info.get("distribution_base", project.name)
# The executable name.
executable_name = project.info.get("executable_name", project.name)
# Extensions to exclude.
ignore_extensions = project.info.get("ignore_extensions", "~ .bak")
# Documentation extensions.
documentation_extensions = project.info.get("documentation_extensions", "txt html")
# Prompt the user for all of the above.
while True:
set_tooltip("")
screen()
ui.vbox()
title(_(u"Building Distributions"))
text_variable(_(u"Base Name:"), base_name, "base_name",
_(u"Used to generate the names of directories and archive files."))
text_variable(_(u"Executable Name:"), executable_name, "executable_name",
_(u"Used to generate the names of executables and runnable programs."))
text_variable(_(u"Ignore Extensions:"), ignore_extensions, "ignore_extensions",
_(u"Files with these extensions will not be included in the distributions."))
text_variable(_(u"Documentation Extensions:"), documentation_extensions, "documentation_extensions",
_(u"Files with these extensions will be treated as documentation, when building the Macintosh application."))
text(_(u"Distributions to Build:"))
if has_windows:
toggle_button(_(u"Windows x86"), build_windows, ui.returns("build_windows"),
_(u"Zip distribution for the 32-bit Windows platform."))
if has_linux:
toggle_button(_(u"Linux x86"), build_linux, ui.returns("build_linux"),
_(u"Tar.Bz2 distribution for the Linux x86 platform."))
if has_mac:
toggle_button(_(u"Macintosh Universal"), build_mac, ui.returns("build_mac"),
_(u"Single application distribution for the Macintosh x86 and ppc platforms."))
if has_all:
toggle_button(_(u"Windows/Linux/Mac Combined"), build_all, ui.returns("build_all"),
_(u"Zip distribution for the Windows x86, Linux x86, Macintosh x86 and Macintosh ppc platforms."))
ui.null(height=15)
button(_(u"Build"), ui.returns("build"), _(u"Start building the distributions."))
button(_(u"Cancel"), ui.jumps("top"), "")
ui.close()
act = interact()
if act == "build_windows":
build_windows = not build_windows
elif act == "build_linux":
build_linux = not build_linux
elif act == "build_mac":
build_mac = not build_mac
elif act == "build_all":
build_all = not build_all
elif act == "base_name":
base_name = input(
_(u"Base Name"),
_(u"Please enter in the base name for your distribution. This name is used to generate the names of directories and archive files. Usually, this is the name of your game, plus a version number, like \"moonlight-1.0\"."),
base_name)
elif act == "executable_name":
executable_name = input(
_(u"Executable Name"),
_(u"Please enter a name for the executables in your distribution. This should not include an extension, as that will be added automatically."),
executable_name)
elif act == "ignore_extensions":
ignore_extensions = input(
_(u"Ignore Extensions"),
_(u"Please enter a space-separated list of file extensions. Files with these extensions will not be included in the built distributions."),
ignore_extensions)
elif act == "documentation_extensions":
documentation_extensions = input(
_(u"Documentation Extensions"),
_(u"Please enter a space separated list of documentation extensions. Files in the base directory with these extensions will have a second copy stored outside of the Macintosh application."),
documentation_extensions)
elif act == "build":
break
# Store the user-selected options in info, and save info.
project.info["distribution_base"] = base_name
project.info["executable_name"] = executable_name
project.info["ignore_extensions"] = ignore_extensions
project.info["documentation_extensions"] = documentation_extensions
project.info["build_windows"] = build_windows
project.info["build_linux"] = build_linux
project.info["build_mac"] = build_mac
project.info["build_all"] = build_all
project.save()
distribute.distribute(
project.path,
config.renpy_base,
base_name=base_name,
executable_name=executable_name,
ignore_extensions=ignore_extensions,
documentation_extensions=documentation_extensions,
build_windows=build_windows,
build_mac=build_mac,
build_linux=build_linux,
build_all=build_all)
# Report success to the user.
set_tooltip(_(u"Thank you for choosing Ren'Py."))
screen()
ui.vbox()
title(_(u"Success"))
text(_(u"The distributions have been built. Be sure to test them before release.\n\nNote that unpacking and repacking the Macintosh, Linux, or Combined distributions on Windows is not supported.\n\nPlease announce your release at the {a=http://lemmasoft.renai.us/forums/}Lemma Soft Forums{/a}, and add it to {a=http://games.renpy.org}games.renpy.org{/a}."))
ui.null(height=20)
button(_(u"Return"), ui.jumps("top"), None)
ui.close()
interact()
-119
View File
@@ -1,119 +0,0 @@
# This file contains logic for selecting an editor.
init python:
import glob
import re
import traceback
import os
import os.path
# The default name for the editor.
if persistent.editor is None:
persistent.editor = "jEdit"
# Should we set up the editor?
set_editor = "RENPY_EDIT_PY" not in os.environ
# A map from editor name to EditorInfo object.
editors = { }
class EditorInfo(object):
def __init__(self, filename):
# The path to the editor info file.
self.filename = filename
# The name of the editor.
self.name = os.path.basename(filename)[:-len(".edit.py")]
# The time the editor file was last modified. We use this
# to decide if we should update the editors mat when we
# have multiple versions of an editor in contention.
self.mtime = os.path.getmtime(filename)
def scan_editor(filename):
"""
Inserts an editor into editors if there isn't a newer
editor there already.
"""
ei = EditorInfo(filename)
if ei.name in editors:
if editors[ei.name].mtime >= ei.mtime:
return
editors[ei.name] = ei
def scan_editors():
"""
Finds all *.edit.py files, and uses them to populate the list
of editors.
"""
editors.clear()
for d in [ config.renpy_base, persistent.projects_directory ]:
if d is None:
continue
for filename in glob.glob(d + "/*/*.edit.py"):
scan_editor(filename)
def setup_editor():
"""
Sets the system up to respect the value containined in
persistent.editor.
"""
if not set_editor:
return
ei = None
for i in [ persistent.editor, "jEdit", "None" ]:
if i in editors:
ei = editors[i]
break
else:
return
os.environ["RENPY_EDIT_PY"] = renpy.fsencode(os.path.abspath(ei.filename))
renpy.editor.init()
label editor:
python hide:
if not set_editor:
error(_(u"The editor has been set from the RENPY_EDIT_PY environment variable, and cannot be changed."), "options")
set_tooltip("")
screen()
ui.vbox()
title(_(u"Choose Editor"))
text(_(u"Please choose the editor that will be use to edit scripts and display errors. More editors can be downloaded from {a=http://www.renpy.org/wiki/renpy/Editors}the Ren'Py website{/a}."))
ui.null(height=15)
scrolled("options")
ui.vbox()
for i in sorted(editors, key=lambda a : a.lower()):
button(i, ui.returns(i))
ui.close() # Vbox
ui.close() # Scrolled
ui.close() # Vbox
persistent.editor = interact()
setup_editor()
jump options
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

-366
View File
@@ -1,366 +0,0 @@
# This file contains the various components of the launcher interface.
init -11 python:
style.launcher_base = Style(style.default)
style.launcher_text = Style(style.launcher_base)
style.launcher_text.size = 15
style.launcher_text.color = "#333"
style.launcher_text.justify = True
style.launcher_title = Style(style.launcher_base)
style.launcher_title.size = 26
style.launcher_title.color = "#333"
style.launcher_button_text = Style(style.launcher_base)
style.launcher_button_text.size = 20
style.launcher_button_text.color = "#03c"
style.launcher_button_text.hover_color = "#088"
style.launcher_button_text.insensitive_color = "#aaa"
style.launcher_button_text.minwidth = 250
style.launcher_small_button_text = Style(style.launcher_button_text)
style.launcher_small_button_text.size = 15
style.launcher_tooltip = Style(style.default)
style.launcher_tooltip.color = "#fff"
style.launcher_tooltip.size = 14
init 11 python hide:
props = { }
for i in style.default.properties:
props.update(i)
if props["font"] == "DejaVuSans.ttf":
style.launcher_base.setdefault(font="DejaVuSerif.ttf")
init 11 python:
import time
# Settings.
config.developer = True
# Make backup of styles.
style_backup = renpy.style.backup()
# Style customizations. These need to be in a function so that we can
# change them when we're choosing a theme.
def customize_styles():
style.window.yminimum = 60
style.window.left_padding = 60
style.hyperlink_text.color = "#03c"
style.hyperlink_text.hover_color = "#00c"
style.hyperlink_text.size = 15
style.hyperlink_text.underline = False
customize_styles()
tooltip = _(u"Welcome!")
def show_tooltip(st, at):
return Text(tooltip, color="#fff", size=14), None
def set_tooltip(s):
global tooltip
if tooltip != s:
tooltip = s
renpy.restart_interaction()
tooltips = renpy.curry(set_tooltip)
def unset_tooltip(s):
global tooltip
if s and tooltip == s:
tooltip = ""
renpy.restart_interaction()
untooltips = renpy.curry(unset_tooltip)
def screen():
"""
Display a screen. This should be called before any code that
draws to the screen.
"""
ui.add(Solid("#fff"))
ui.window(yalign=1.0)
ui.add(DynamicDisplayable(show_tooltip))
ui.image("eileen_small.png", xalign=0.0, yalign=1.0)
ui.window(xmargin=20, top_margin=4, bottom_margin=85, style='default')
def interact():
"""
Closes the screen, and causes an interaction to occur.
"""
return ui.interact(suppress_overlay=True, suppress_underlay=True)
def title(s):
"""
Display a title on the screen.
"""
ui.window(style="default", bottom_margin=4, top_margin=12)
ui.text(s, style="launcher_title")
def text(s):
"""
Display text on the screen.
"""
ui.text(s, style="launcher_text")
def button(s, clicked=None, subtitle="", hovered=None, unhovered=None):
"""
Displays a button with caption `s`.
"""
if subtitle is not None:
if hovered is None:
hovered = tooltips(subtitle)
if unhovered is None:
unhovered = untooltips(subtitle)
ui.button(style="default", clicked=clicked,
hovered=hovered, unhovered=unhovered,
top_padding=3, bottom_padding=3)
ui.text(s, style="launcher_button_text")
def small_button(s, clicked=None, subtitle="", hovered=None, unhovered=None):
"""
Displays a button with caption `s`.
"""
if hovered is None:
hovered = tooltips(subtitle)
if unhovered is None:
unhovered = untooltips(subtitle)
ui.button(style="default", clicked=clicked,
hovered=hovered, unhovered=unhovered,
top_padding=0, bottom_padding=0)
ui.text(s, style="launcher_small_button_text")
def toggle_button(s, checked, clicked=None, subtitle=""):
"""
Displays a button with caption `s`.
"""
hovered = tooltips(subtitle)
unhovered = untooltips(subtitle)
if checked:
s = u"{font=DejaVuSans.ttf}\u25a3{/font} " + s
else:
s = u"{font=DejaVuSans.ttf}\u25a1{/font} " + s
ui.button(style="default", clicked=clicked,
hovered=hovered, unhovered=unhovered,
top_padding=0, bottom_padding=0)
ui.text(s, style="launcher_small_button_text")
def scrolled(cancel, yadj=None):
if yadj is None:
yadj = ui.adjustment()
ui.side(['r', 'b', 'c'])
ui.bar(adjustment=yadj, style='lscrollbar')
if cancel:
ui.vbox()
ui.null(height=12)
button(_(u"Cancel"), clicked=ui.jumps(cancel))
ui.close()
else:
ui.null()
ui.viewport(yadjustment=yadj, mousewheel=True)
# Left up to the user to close.
def error(message, target="top"):
"""
Displays an error to the user, and lets him click to return to
`target`.
"""
set_tooltip("")
screen()
ui.vbox()
title(_(u"Error"))
text(message)
ui.null(height=20)
if target is None:
clicked = ui.returns(True)
else:
clicked = ui.jumps(target)
button(_(u"Return"), clicked, None)
ui.close()
interact()
def pauseinfo(t, message):
"""
Displays an error to the user, and lets him click to return to
`target`.
"""
set_tooltip("")
screen()
ui.vbox()
title(t)
text(message)
ui.null(height=20)
button(_(u"Ok"), ui.returns(True), None)
ui.close()
interact()
def info(t, message):
"""
Displays an informational message to the user, and immediately
returns.
"""
ui.pausebehavior(0)
screen()
ui.vbox()
title(t)
text(message)
ui.close()
interact()
def yesno(t, message):
"""
Asks a yes/no question of the user.
"""
set_tooltip("")
screen()
ui.vbox()
title(t)
text(message)
button(_(u"Yes"), ui.returns(True), "")
button(_(u"No"), ui.returns(False), "")
ui.close()
return interact()
def text_variable(t, value, returns, tooltip=""):
text(t)
small_button(value, ui.returns(returns), tooltip)
ui.null(height=5)
def input(t, prompt, value, cancel=None):
set_tooltip(_(u"Press enter when done."))
while True:
screen()
ui.vbox()
title(t)
text(prompt)
ui.null(height=10)
ui.input(value, size=20, color="#00c")
if cancel is not None:
ui.null(height=20)
button(_(u"Cancel"), ui.jumps(cancel), "")
ui.close()
value = interact()
value = value.strip()
if not value:
error(
_(u"The string cannot be empty. Please enter some text."),
None)
continue
try:
value = value.encode("ascii")
except:
error(
_(u"Non-ASCII filenames are not allowed. This is because Zip files cannot reliably represent non-ASCII filenames."),
None)
continue
break
return value
# The time progress was last updated. We only update the progress
# every 10th of a second, so that we don't waste time doing
# such updates.
progress_time = 0
def progress(what, limit, amount):
"""
Show progress to the user.
"""
global progress_time
t = time.time()
if t < progress_time + .1:
return
progress_time = t
set_tooltip(_(u"Processed %d of %d files.") % (amount + 1, limit))
ui.pausebehavior(0)
screen()
ui.vbox(xfill=True)
title(what)
ui.null(height=20)
ui.bar(limit, amount, xmaximum=300, xalign=0.5, style='lbar')
ui.close()
interact()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

-52
View File
@@ -1,52 +0,0 @@
init python:
import os
import os.path
import shutil
label new_project:
if persistent.projects_directory is None:
call choose_projects_directory
python hide:
name = input(
_(u"New Project"),
_(u"Please type the name of your new project."),
"", cancel='top')
name = name.strip()
path = os.path.join(persistent.projects_directory, name)
set_tooltip("")
if os.path.exists(path):
error(_(u"Something with that name already exists in the projects directory."))
info(_(u"Creating Project"),
_(u"Please wait while we create the project."))
template = os.path.join(config.renpy_base, "template")
try:
shutil.copytree(template, path)
except OSError, e:
error(_(u"Could not create the project directory. The error was: %s") % unicode(e))
launcherinfo = os.path.join(path, "launcherinfo.py")
if os.path.exists(launcherinfo):
os.unlink(launcherinfo)
# Change the save directory.
options = file(path + "/game/options.rpy").read()
save_dir = "%s-%d" % (name, time.time())
options = options.replace("template-1220804310", save_dir)
file(path + "/game/options.rpy", "w").write(options)
p = Project(path)
p.select()
jump choose_theme
-136
View File
@@ -1,136 +0,0 @@
## This file contains some of the options that can be changed to customize
## your Ren'Py game. It only contains the most common options... there
## is quite a bit more customization you can do.
##
## Lines beginning with two '#' marks are comments, and you shouldn't
## uncomment them. Lines beginning with a single '#' mark are
## commented-out code, and you may want to uncomment them when
## appropriate.
init -1 python hide:
## Should we enable the use of developer tools? This should be
## set to False before the game is released, so the user can't
## cheat using developer tools.
config.developer = True
## These control the width and height of the screen.
config.screen_width = 540
config.screen_height = 440
## This controls the title of the window, when Ren'Py is
## running in a window.
config.window_title = _(u"%s Launcher") % renpy.version()
#########################################
# Themes
## We then want to call a theme function. themes.roundrect is
## a theme that features the use of rounded rectangles. It's
## the only theme we currently support.
##
## The theme function takes a number of parameters that can
## customize the color scheme.
theme.roundrect(
## The color of an idle widget face.
widget = "#003c78",
## The color of a focused widget face.
widget_hover = "#0050a0",
## The color of the text in a widget.
widget_text = "#c8ffff",
## The color of the text in a selected widget. (For
## example, the current value of a preference.)
widget_selected = "#ffffc8",
## The color of a disabled widget face.
disabled = "#404040",
## The color of disabled widget text.
disabled_text = "#c8c8c8",
## The color of informational labels.
label = "#ffffff",
## The color of a frame containing widgets.
frame = "#6496c8",
## If this is True, the in-game window is rounded. If False,
## the in-game window is square.
rounded_window = False,
## The background of the main menu. This can be a color
## beginning with '#', or an image filename. The latter
## should take up the full height and width of the screen.
mm_root = "#dcebff",
## The background of the game menu. This can be a color
## beginning with '#', or an image filename. The latter
## should take up the full height and width of the screen.
gm_root = "#dcebff",
## And we're done with the theme. The theme will customize
## various styles, so if we want to change them, we should
## do so below.
)
theme.outline_bars(
inside="#6496c8",
idle="#06c",
hover="#00c")
# Copy the bar styles used by the launcher, so the theme
# selector can't change them.
style.lbar = Style(style.default)
style.lbar.take(style.bar)
style.lscrollbar = Style(style.default)
style.lscrollbar.take(style.vscrollbar)
#########################################
## These settings let you change some of the sounds that are used by
## Ren'Py.
## Set this to False if the game does not have any sound effects.
config.sound = False
# Disable opengl.
config.gl_enable = False
# Disable logging.
config.log_enable = False
python early:
config.save_directory = "launcher-2"
init -1 python hide:
#########################################
## Default values of Preferences.
## Note: These options are only evaluated the first time a
## game is run. To have them run a second time, delete
## game/saves/persistent
## Should we start in fullscreen mode?
config.default_fullscreen = False
## The default text speed in characters per second. 0 is infinite.
config.default_text_cps = 0
#########################################
## More customizations can go here.
config.has_autosave = False
config.window_icon = "logo.png"
config.windows_icon = "logo32.png"
config.new_substitutions = False
-42
View File
@@ -1,42 +0,0 @@
# This file handles the launcher options screen.
label options:
python hide:
screen()
ui.vbox()
title(_(u"Launcher Options"))
editor = persistent.editor
if not set_editor:
editor = _(u"Using RENPY_EDIT_PY")
text_variable(_("Text Editor"), editor, "editor",
_(u"Change the default text editor."))
ui.null(height=15)
button(_(u"Projects Directory"),
ui.jumps("options_cpd"),
_(u"Select the directory Ren'Py searches for projects."))
ui.null(height=15)
button(_(u"Return"), ui.jumps("top"), "")
ui.close()
act = interact()
if act == "editor":
renpy.jump("editor")
jump options
label options_cpd:
call choose_projects_directory
jump options
-3914
View File
File diff suppressed because it is too large Load Diff
-454
View File
@@ -1,454 +0,0 @@
#@PydevCodeAnalysisIgnore
"""Common operations on Posix pathnames.
Instead of importing this module directly, import os and refer to
this module as os.path. The "os.path" name is an alias for this
module on Posix systems; on other systems (e.g. Mac, Windows),
os.path provides the same operations in a manner specific to that
platform, and is an alias to another module (e.g. macpath, ntpath).
Some of this can actually be useful on non-Posix systems too, e.g.
for manipulation of the pathname component of URLs.
"""
import os
import stat
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
"basename","dirname","commonprefix","getsize","getmtime",
"getatime","getctime","islink","exists","lexists","isdir","isfile",
"ismount","walk","expanduser","expandvars","normpath","abspath",
"samefile","sameopenfile","samestat",
"curdir","pardir","sep","pathsep","defpath","altsep","extsep",
"devnull","realpath","supports_unicode_filenames"]
# strings representing various path-related bits and pieces
curdir = '.'
pardir = '..'
extsep = '.'
sep = '/'
pathsep = ':'
defpath = ':/bin:/usr/bin'
altsep = None
devnull = '/dev/null'
# Normalize the case of a pathname. Trivial in Posix, string.lower on Mac.
# On MS-DOS this may also turn slashes into backslashes; however, other
# normalizations (such as optimizing '../' away) are not allowed
# (another function should be defined to do that).
def normcase(s):
"""Normalize case of pathname. Has no effect under Posix"""
return s
# Return whether a path is absolute.
# Trivial in Posix, harder on the Mac or MS-DOS.
def isabs(s):
"""Test whether a path is absolute"""
return s.startswith('/')
# Join pathnames.
# Ignore the previous parts if a part is absolute.
# Insert a '/' unless the first part is empty or already ends in '/'.
def join(a, *p):
"""Join two or more pathname components, inserting '/' as needed"""
path = a
for b in p:
if b.startswith('/'):
path = b
elif path == '' or path.endswith('/'):
path += b
else:
path += '/' + b
return path
# Split a path in head (everything up to the last '/') and tail (the
# rest). If the path ends in '/', tail will be empty. If there is no
# '/' in the path, head will be empty.
# Trailing '/'es are stripped from head unless it is the root.
def split(p):
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is
everything after the final slash. Either part may be empty."""
i = p.rfind('/') + 1
head, tail = p[:i], p[i:]
if head and head != '/'*len(head):
head = head.rstrip('/')
return head, tail
# Split a path in root and extension.
# The extension is everything starting at the last dot in the last
# pathname component; the root is everything before that.
# It is always true that root + ext == p.
def splitext(p):
"""Split the extension from a pathname. Extension is everything from the
last dot to the end. Returns "(root, ext)", either part may be empty."""
i = p.rfind('.')
if i<=p.rfind('/'):
return p, ''
else:
return p[:i], p[i:]
# Split a pathname into a drive specification and the rest of the
# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
def splitdrive(p):
"""Split a pathname into drive and path. On Posix, drive is always
empty."""
return '', p
# Return the tail (basename) part of a path.
def basename(p):
"""Returns the final component of a pathname"""
return split(p)[1]
# Return the head (dirname) part of a path.
def dirname(p):
"""Returns the directory component of a pathname"""
return split(p)[0]
# Return the longest prefix of all list elements.
def commonprefix(m):
"Given a list of pathnames, returns the longest common leading component"
if not m: return ''
s1 = min(m)
s2 = max(m)
n = min(len(s1), len(s2))
for i in xrange(n):
if s1[i] != s2[i]:
return s1[:i]
return s1[:n]
# Get size, mtime, atime of files.
def getsize(filename):
"""Return the size of a file, reported by os.stat()."""
return os.stat(filename).st_size
def getmtime(filename):
"""Return the last modification time of a file, reported by os.stat()."""
return os.stat(filename).st_mtime
def getatime(filename):
"""Return the last access time of a file, reported by os.stat()."""
return os.stat(filename).st_atime
def getctime(filename):
"""Return the metadata change time of a file, reported by os.stat()."""
return os.stat(filename).st_ctime
# Is a path a symbolic link?
# This will always return false on systems where os.lstat doesn't exist.
def islink(path):
"""Test whether a path is a symbolic link"""
try:
st = os.lstat(path)
except (os.error, AttributeError):
return False
return stat.S_ISLNK(st.st_mode)
# Does a path exist?
# This is false for dangling symbolic links.
def exists(path):
"""Test whether a path exists. Returns False for broken symbolic links"""
try:
st = os.stat(path)
except os.error:
return False
return True
# Being true for dangling symbolic links is also useful.
def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
st = os.lstat(path)
except os.error:
return False
return True
# Is a path a directory?
# This follows symbolic links, so both islink() and isdir() can be true
# for the same path.
def isdir(path):
"""Test whether a path is a directory"""
try:
st = os.stat(path)
except os.error:
return False
return stat.S_ISDIR(st.st_mode)
# Is a path a regular file?
# This follows symbolic links, so both islink() and isfile() can be true
# for the same path.
def isfile(path):
"""Test whether a path is a regular file"""
try:
st = os.stat(path)
except os.error:
return False
return stat.S_ISREG(st.st_mode)
# Are two filenames really pointing to the same file?
def samefile(f1, f2):
"""Test whether two pathnames reference the same actual file"""
s1 = os.stat(f1)
s2 = os.stat(f2)
return samestat(s1, s2)
# Are two open files really referencing the same file?
# (Not necessarily the same file descriptor!)
def sameopenfile(fp1, fp2):
"""Test whether two open file objects reference the same file"""
s1 = os.fstat(fp1)
s2 = os.fstat(fp2)
return samestat(s1, s2)
# Are two stat buffers (obtained from stat, fstat or lstat)
# describing the same file?
def samestat(s1, s2):
"""Test whether two stat buffers reference the same file"""
return s1.st_ino == s2.st_ino and \
s1.st_dev == s2.st_dev
# Is a path a mount point?
# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
def ismount(path):
"""Test whether a path is a mount point"""
try:
s1 = os.lstat(path)
s2 = os.lstat(join(path, '..'))
except os.error:
return False # It doesn't exist -- so not a mount point :-)
dev1 = s1.st_dev
dev2 = s2.st_dev
if dev1 != dev2:
return True # path/.. on a different device as path
ino1 = s1.st_ino
ino2 = s2.st_ino
if ino1 == ino2:
return True # path/.. is the same i-node as path
return False
# Directory tree walk.
# For each directory under top (including top itself, but excluding
# '.' and '..'), func(arg, dirname, filenames) is called, where
# dirname is the name of the directory and filenames is the list
# of files (and subdirectories etc.) in the directory.
# The func may modify the filenames list, to implement a filter,
# or to impose a different order of visiting.
def walk(top, func, arg):
"""Directory tree walk with callback function.
For each directory in the directory tree rooted at top (including top
itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
dirname is the name of the directory, and fnames a list of the names of
the files and subdirectories in dirname (excluding '.' and '..'). func
may modify the fnames list in-place (e.g. via del or slice assignment),
and walk will only recurse into the subdirectories whose names remain in
fnames; this can be used to implement a filter, or to impose a specific
order of visiting. No semantics are defined for, or required of, arg,
beyond that arg is always passed to func. It can be used, e.g., to pass
a filename pattern, or a mutable object designed to accumulate
statistics. Passing None for arg is common."""
try:
names = os.listdir(top)
except os.error:
return
func(arg, top, names)
for name in names:
name = join(top, name)
try:
st = os.lstat(name)
except os.error:
continue
if stat.S_ISDIR(st.st_mode):
walk(name, func, arg)
# Expand paths beginning with '~' or '~user'.
# '~' means $HOME; '~user' means that user's home directory.
# If the path doesn't begin with '~', or if the user or $HOME is unknown,
# the path is returned unchanged (leaving error reporting to whatever
# function is called with the expanded path as argument).
# See also module 'glob' for expansion of *, ? and [...] in pathnames.
# (A function should also be defined to do full *sh-style environment
# variable expansion.)
def expanduser(path):
"""Expand ~ and ~user constructions. If user or $HOME is unknown,
do nothing."""
if not path.startswith('~'):
return path
i = path.find('/', 1)
if i < 0:
i = len(path)
if i == 1:
if 'HOME' not in os.environ:
import pwd
userhome = pwd.getpwuid(os.getuid()).pw_dir
else:
userhome = os.environ['HOME']
else:
import pwd
try:
pwent = pwd.getpwnam(path[1:i])
except KeyError:
return path
userhome = pwent.pw_dir
userhome = userhome.rstrip('/')
return userhome + path[i:]
# Expand paths containing shell variable substitutions.
# This expands the forms $variable and ${variable} only.
# Non-existent variables are left unchanged.
_varprog = None
def expandvars(path):
"""Expand shell variables of form $var and ${var}. Unknown variables
are left unchanged."""
global _varprog
if '$' not in path:
return path
if not _varprog:
import re
_varprog = re.compile(r'\$(\w+|\{[^}]*\})')
i = 0
while True:
m = _varprog.search(path, i)
if not m:
break
i, j = m.span(0)
name = m.group(1)
if name.startswith('{') and name.endswith('}'):
name = name[1:-1]
if name in os.environ:
tail = path[j:]
path = path[:i] + os.environ[name]
i = len(path)
path += tail
else:
i = j
return path
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
# It should be understood that this may change the meaning of the path
# if it contains symbolic links!
def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
if path == '':
return '.'
initial_slashes = path.startswith('/')
# POSIX allows one or two initial slashes, but treats three or more
# as single slash.
if (initial_slashes and
path.startswith('//') and not path.startswith('///')):
initial_slashes = 2
comps = path.split('/')
new_comps = []
for comp in comps:
if comp in ('', '.'):
continue
if (comp != '..' or (not initial_slashes and not new_comps) or
(new_comps and new_comps[-1] == '..')):
new_comps.append(comp)
elif new_comps:
new_comps.pop()
comps = new_comps
path = '/'.join(comps)
if initial_slashes:
path = '/'*initial_slashes + path
return path or '.'
def abspath(path):
"""Return an absolute path."""
if not isabs(path):
path = join(os.getcwd(), path)
return normpath(path)
# Return a canonical path (i.e. the absolute location of a file on the
# filesystem).
def realpath(filename):
"""Return the canonical path of the specified filename, eliminating any
symbolic links encountered in the path."""
if isabs(filename):
bits = ['/'] + filename.split('/')[1:]
else:
bits = [''] + filename.split('/')
for i in range(2, len(bits)+1):
component = join(*bits[0:i])
# Resolve symbolic links.
if islink(component):
resolved = _resolve_link(component)
if resolved is None:
# Infinite loop -- return original component + rest of the path
return abspath(join(*([component] + bits[i:])))
else:
newpath = join(*([resolved] + bits[i:]))
return realpath(newpath)
return abspath(filename)
def _resolve_link(path):
"""Internal helper function. Takes a path and follows symlinks
until we either arrive at something that isn't a symlink, or
encounter a path we've seen before (meaning that there's a loop).
"""
paths_seen = []
while islink(path):
if path in paths_seen:
# Already seen this path, so we must have a symlink loop
return None
paths_seen.append(path)
# Resolve where the link points to
resolved = os.readlink(path)
if not isabs(resolved):
dir = dirname(path)
path = normpath(join(dir, resolved))
else:
path = normpath(resolved)
return path
supports_unicode_filenames = False
-334
View File
@@ -1,334 +0,0 @@
init python:
import os
import os.path
import sys
import platform
import subprocess
# Set the projects directory to None if it no longer exists.
if persistent.projects_directory is not None:
if not os.path.isdir(persistent.projects_directory):
persistent.projects_directory = None
ZWSP = u"\u200B"
# The process of the running game.
game_proc = None
class ProcessBehavior(renpy.display.layout.Null):
def __init__(self, proc):
super(ProcessBehavior, self).__init__()
self.proc = proc
def event(self, ev, x, y, st):
if self.proc.poll() is not None:
return True
renpy.game.interface.timeout(.25)
def quote_char(c):
n = ord(c)
if 0x20 <= n <= 0x7f:
return c
else:
return "\\x%02x" % n
# Quotes an arbitrary string (without knowing the encoding) for display.
def quote(s):
s = "".join(quote_char(i) for i in s)
s = s.replace("[", "[[")
s = s.replace("/", "/" + ZWSP)
s = s.replace("\\", "\\" + ZWSP)
return s
class Project(object):
def __init__(self, path):
# The name of the project.
self.name = os.path.basename(path)
# The full path to the project.
self.path = path
# The path to the game directory, for convenience.
self.gamedir = os.path.join(path, "game")
def __repr__(self):
return "<Project %r>" % (self.path)
def select(self):
"""
Select this project as the one that we're working on.
"""
global project
global game_proc
persistent.project_path = self.path
project = self
game_proc = None
# Load the information dictionary.
info = dict()
launcherinfo = os.path.join(self.path, "launcherinfo.py")
if os.path.exists(launcherinfo):
source = file(launcherinfo, "rU").read().decode("utf8")
if source[0] == u'\ufeff':
source = source[1:]
source = source.encode("raw_unicode_escape")
exec source in info
del info["__builtins__"]
self.info = info
def save(self):
"""
Saves the info dictionary into the launcherinfo file.
"""
launcherinfo = os.path.join(self.path, "launcherinfo.py")
f = file(launcherinfo + ".new", "w")
for k, v in self.info.iteritems():
f.write("%s = %r\n" % (k, v))
f.close()
try:
os.rename(launcherinfo + ".new", launcherinfo)
except:
os.unlink(launcherinfo)
os.rename(launcherinfo + ".new", launcherinfo)
def scan_projects():
"""
Scans for projects. Returns a list of Project objects.
"""
rv = [ ]
project_dirs = [ config.renpy_base ]
if persistent.projects_directory and persistent.projects_directory not in project_dirs:
project_dirs.append(persistent.projects_directory)
for d in project_dirs:
if not os.path.isdir(d):
continue
for pd in sorted(os.listdir(d), key=lambda a : a.lower()):
path = os.path.join(d, pd)
if not os.path.isdir(path):
continue
gamedir = os.path.join(path, "game")
if not os.path.isdir(gamedir):
continue
rv.append(Project(path))
return rv
def choose_default_project():
"""
Chooses the default project, based on the persistent information.
"""
project = None
projects = scan_projects()
for i in projects:
if i.path == persistent.project_path:
i.select()
return
for i in projects:
if i.name == "tutorial":
i.select()
return
if projects:
projects[0].select()
return
def select_project(p):
p.select()
renpy.jump("top")
def FsPopen(cmd, **kwargs):
return subprocess.Popen([ renpy.fsencode(i) for i in cmd], **kwargs)
curried_select_project = renpy.curry(select_project)
label select_project:
if persistent.projects_directory is None:
call choose_projects_directory
python:
screen()
ui.vbox()
title(_(u"Select Project"))
scrolled("top")
ui.vbox()
projects = scan_projects()
for i in projects:
button(i.name,
curried_select_project(i),
quote(i.path))
ui.close() # vbox
ui.close() # scrolled
ui.close() # vbox
interact()
label launch_tutorial:
python hide:
if sys.platform == "win32" and sys.argv[0].lower().endswith(".exe"):
proc = FsPopen([sys.argv[0], tutorial_path])
else:
proc = FsPopen([sys.executable, sys.argv[0], tutorial_path])
set_tooltip(_(u"Tutorial game has been launched."))
jump top
label launch:
python hide:
if sys.platform == "win32" and sys.argv[0].lower().endswith(".exe"):
proc = FsPopen([sys.argv[0], project.path])
else:
proc = FsPopen([sys.executable, sys.argv[0], project.path])
set_tooltip(_(u"%s has been launched.") % project.name.capitalize())
store.game_proc = proc
jump top
label game_directory:
python hide:
gamedir = os.path.normpath(project.gamedir)
if sys.platform == "win32":
os.startfile(gamedir)
elif platform.mac_ver()[0]:
FsPopen([ "open", gamedir ])
else:
FsPopen([ "xdg-open", gamedir ])
gamedir = quote(gamedir)
set_tooltip(_(u"Opening game directory:\n%s") % gamedir)
jump top
label edit_script:
python hide:
if not "RENPY_EDIT_PY" in os.environ:
error(_(u"No editor has been selected."))
files = [ project.gamedir + "/" + i for i in os.listdir(project.gamedir) if i.endswith(".rpy") if not i[0] == "."]
files.sort()
for i in files[:]:
if i.endswith("options.rpy"):
files.remove(i)
files.insert(0, i)
for i in files[:]:
if i.endswith("script.rpy"):
files.remove(i)
files.insert(0, i)
if not files:
error(_(u"No files to edit."))
if not renpy.launch_editor(files):
error(_(u"Launching the editor failed."))
set_tooltip(_(u"Launched editor with %d script files.") % len(files))
jump top
label lint:
python hide:
set_tooltip("")
info(_(u"Lint"), _(u"Lint in progress."))
lf = file("lint.txt", "w+")
if hasattr(sys, "winver") and sys.argv[0].lower().endswith(".exe"):
CREATE_NO_WINDOW=0x08000000
proc = FsPopen([config.renpy_base + "/console.exe", "--lint", project.path], stdin=lf, stdout=lf, stderr=lf, creationflags=CREATE_NO_WINDOW)
else:
proc = FsPopen([sys.executable, sys.argv[0], "--lint", project.path], stdout=lf)
proc.wait()
lf.close()
renpy.launch_editor([ "lint.txt" ], transient=1)
return
label call_lint:
call lint
python:
set_tooltip(_(u"Lint complete."))
jump top
label delete_persistent:
python hide:
set_tooltip("")
info(_(u"Delete Persistent"), _(u"Deleting persistent data."))
if hasattr(sys, "winver") and sys.argv[0].lower().endswith(".exe"):
proc = FsPopen([config.renpy_base + "/console.exe", "--rmpersistent", project.path])
else:
proc = FsPopen([sys.executable, sys.argv[0], "--rmpersistent", project.path])
proc.wait()
set_tooltip(_(u"Persistent data has been deleted."))
jump top
-68
View File
@@ -1,68 +0,0 @@
init python:
import os
import subprocess
try:
import EasyDialogs
except ImportError:
try:
import EasyDialogsWin as EasyDialogs
except:
EasyDialogs = None
label choose_projects_directory:
python hide:
set_tooltip("")
info(_(u"Choose Projects Directory"),
_(u"Please choose the directory containing your projects."))
path = persistent.projects_directory
if path is None:
path = os.path.dirname(config.renpy_base)
path = os.environ.get("RENPY_DEFAULT_PROJECTS_DIRECTORY", path)
if EasyDialogs:
choice = EasyDialogs.AskFolder(defaultLocation=renpy.fsencode(path), wanted=str)
if choice is not None:
path = renpy.fsdecode(choice)
else:
try:
env = os.environ.copy()
if 'RENPY_OLD_LD_LIBRARY_PATH' in env:
env['LD_LIBRARY_PATH'] = env['RENPY_OLD_LD_LIBRARY_PATH']
zen = subprocess.Popen([
"zenity",
"--title=Select Projects Directory",
"--file-selection",
"--directory",
"--filename=" + renpy.fsencode(path) ], env=env, stdout=subprocess.PIPE)
choice = zen.stdout.read()
zen.wait()
if choice:
path = renpy.fsdecode(choice[:-1])
except:
error(_(u"Could not run zenity. The projects directory has been set to the directory immediately above the directory containing Ren'Py."), None)
persistent.projects_directory = path
choose_default_project()
return
-47
View File
@@ -1,47 +0,0 @@
def k(s):
s = s.replace(" ", "")
s = s.replace(":", "")
s = s.replace("\n", "")
return int(s, 16)
modulus = k("""
00:c2:e6:31:c6:7d:b3:cf:57:12:91:cf:b4:2c:9d:
25:17:cf:21:f8:ac:5b:1d:c1:95:56:9d:1c:1c:d5:
eb:67:ca:0a:a4:3f:e5:9c:6c:10:6e:4a:7a:2c:c2:
d6:e3:3f:84:6d:b1:6f:70:3d:05:4e:06:91:3c:51:
28:3f:b1:26:a5:be:6e:a6:14:a1:09:95:87:10:4d:
c3:4b:44:fd:e0:a6:9d:f8:1f:50:ca:8a:0d:fd:86:
7d:39:74:af:af:4e:05:8b:bd:93:b5:9b:6a:85:ca:
1e:47:25:ca:f1:31:47:10:3e:a2:26:79:d2:00:34:
f4:c5:a8:db:b6:36:2b:70:ef:14:8c:44:41:02:11:
64:31:d4:d8:cd:7b:a7:e1:af:25:2c:a4:31:fb:84:
c7:bc:6e:7c:fb:6a:46:5d:ee:bb:5f:74:96:a1:fe:
13:79:55:e8:c4:74:77:1c:ea:6a:8e:a8:cb:89:da:
20:db:88:18:e5:4a:f9:93:43:03:53:c3:d8:a3:2c:
73:ea:8c:64:55:da:cf:a5:c7:11:70:ce:d4:51:3c:
2c:19:06:3c:23:0c:d6:02:a5:95:28:b7:b0:34:1f:
5d:32:38:e0:01:f2:12:b5:b2:8f:6b:83:83:06:6c:
e0:ca:ec:e5:45:3e:75:79:f4:bf:47:e2:56:cd:8a:
7b:cc:c7:0d:14:af:00:f6:e0:64:e2:30:a3:6c:ed:
05:1e:33:9a:88:8b:0d:31:48:2b:02:65:61:4b:cd:
84:90:c5:83:ba:93:1f:b7:78:7f:ec:4e:e9:bc:02:
03:86:c2:a9:00:87:4c:e1:bb:d8:e7:b2:12:a6:9b:
ab:9f:24:6f:6d:fe:09:60:22:d9:45:c2:20:55:70:
27:9d:3a:84:fb:57:9a:d3:d0:d2:d3:18:25:8c:40:
f8:2c:15:98:de:2e:2d:be:b4:dd:6e:24:2b:07:0b:
21:08:a6:db:30:df:5c:7f:50:6f:be:b7:2e:40:28:
58:85:89:de:4e:c7:66:af:fc:05:1c:c7:4d:d1:d2:
fa:2c:00:1a:0e:47:5f:30:b6:e6:90:fe:99:96:42:
4d:17:d3:13:36:7b:76:1c:36:c6:31:f3:a8:b0:9c:
33:a1:b3:e9:40:51:b9:29:1d:b4:36:2a:20:0e:c0:
f7:fb:a9:db:18:e0:67:ca:5d:d7:46:03:17:32:04:
35:24:5a:fe:0a:1e:7b:96:8b:0a:fb:3c:f6:94:92:
ab:d8:7d:ed:e4:2b:64:d7:ab:8d:11:89:e3:80:9d:
28:59:da:de:6f:55:30:30:78:9a:06:30:5b:a4:ff:
7a:70:49:b6:8c:9a:66:c0:a8:77:74:ca:f5:02:05:
63:8c:83
""")
exponent = 0x10001
-143
View File
@@ -1,143 +0,0 @@
init python:
tutorial_path = config.renpy_base + "/tutorial"
tutorial_exists = os.path.isdir(tutorial_path)
label main_menu:
return
label start:
python:
choose_default_project()
scan_editors()
setup_editor()
jump top
label top:
python:
if game_proc and game_proc.poll() is None:
launch = None
ui.add(ProcessBehavior(game_proc))
else:
launch = ui.jumps("launch")
game_proc = None
screen()
ui.vbox()
ui.viewport(draggable=True, ymaximum=47)
title(project.name.capitalize())
ui.grid(2, 4, transpose=True)
button(_(u"Launch"),
launch,
_(u"Launches the project."))
button(_(u"Edit Script"),
ui.jumps("edit_script"),
_(u"Edits the script of the project."))
button(_(u"Game Directory"),
ui.jumps("game_directory"),
_(u"Opens the project's game directory."))
button(_(u"Check Script (Lint)"),
ui.jumps("call_lint"),
_(u"Checks the script of the project for likely errors."))
button(_(u"Choose Theme"),
ui.jumps("choose_theme"),
_(u"Changes the theme used by the project."))
button(_(u"Delete Persistent"),
ui.jumps("delete_persistent"),
_(u"Deletes the persistent data associated with the project."))
button(_(u"Archive Files"),
ui.jumps("archiver"),
_(u"Archives files found in the game and archived directories."))
button(_(u"Build Distributions"),
ui.jumps("distribute"),
_(u"Builds distributions of the project."))
ui.close()
title(_(u"Ren'Py"))
ui.grid(2, 4, transpose=True)
button(_(u"Select Project"),
ui.jumps("select_project"),
_(u"Select a project to work with."))
button(_(u"New Project"),
ui.jumps("new_project"),
_(u"Create a new project."))
button(_(u"Ren'Py Games List"),
ui.jumps("renpy_games_list"),
_(u"Visit the Ren'Py games list, at http://games.renpy.org."))
button(_(u"Quit"),
ui.jumps("launcher_quit"),
_(u"Causes the launcher to exit."))
button(_(u"Options"),
ui.jumps("options"),
_(u"Change Ren'Py launcher options."))
button(_(u"Ren'Py Help"),
ui.jumps("documentation"),
_(u"Open the Ren'Py documentation in a web browser."))
if tutorial_exists:
button(_(u"Tutorial Game"),
ui.jumps("launch_tutorial"),
_(u"Launches the Ren'Py tutorial game."))
else:
ui.null()
ui.null()
ui.close()
ui.close()
if update_allowed:
ui.key("U", Jump("update"))
interact()
# ProcessBehavior can return True, which sends us here.
jump top
label documentation:
python hide:
import webbrowser
webbrowser.open_new("file:///" + config.renpy_base + "/doc/index.html")
set_tooltip(_(u"Now showing the Ren'Py documentation in your web browser."))
jump top
label renpy_games_list:
python hide:
import webbrowser
webbrowser.open_new("http://games.renpy.org")
set_tooltip(_(u"Now showing the Ren'Py Games List in your web browser."))
jump top
label launcher_quit:
label confirm_quit:
$ renpy.quit()
-4
View File
@@ -1,4 +0,0 @@
init -999:
$ config.script_version = (6, 13, 12)
-217
View File
@@ -1,217 +0,0 @@
init python:
LAUNCHER_RPYS = set(['options.rpy', 'strings.rpy', 'transupdate.rpy', 'choose_theme.rpy', 'archiver.rpy', 'project.rpy', 'script_version.rpy', 'new.rpy', 'updater.rpy', 'themes_data.rpy', 'opts.rpy', 'interface.rpy', 'projectsdir.rpy', 'update_interface.rpy', 'distribute.rpy', 'script.rpy', 'editor.rpy'])
TRANSLATION_STRINGS = [
u"Graphics Acceleration",
u"Automatically Choose",
u"Prefer Software Renderer",
u"Prefer OpenGL Renderer",
u"Continue",
u"Are you sure?",
u"Are you sure you want to delete this save?",
u"Are you sure you want to overwrite your save?",
u"Loading will lose unsaved progress.\nAre you sure you want to do this?",
u"Are you sure you want to quit?",
u"Are you sure you want to return to the main menu?\nThis will lose unsaved progress.",
u"Skip Mode",
u"Fast Skip Mode",
u"Please click to continue.",
u"Start Game",
u"Load Game",
u"Preferences",
u"Help",
u"Quit",
u"Return",
u"Save Game",
u"Main Menu",
u"Begin Skipping",
u"Empty Slot.",
u"Previous",
u"Next",
u"Yes",
u"No",
u"The error message was:",
u"You may want to try saving in a different slot, or playing for a while and trying again later.",
u"Save Failed.",
u"Continue Game",
u"Test",
u"Left",
u"Right",
u"Not Assigned",
u"Up",
u"Down",
u"Select/Dismiss",
u"Joystick Mapping",
u"Move the joystick or press a joystick button to create the mapping. Click the mouse to remove the mapping.",
u"Rollback",
u"Hold to Skip",
u"Toggle Skip",
u"Hide Text",
u"Menu",
u"Display",
u"Window",
u"Fullscreen",
u"Transitions",
u"All",
u"Some",
u"None",
u"Skip",
u"Seen Messages",
u"All Messages",
u"After Choices",
u"Stop Skipping",
u"Keep Skipping",
u"Text Speed",
u"Auto-Forward Time",
u"Music Volume",
u"Sound Volume",
u"Joystick...",
u"Joystick Configuration",
u"Voice Volume",
u"Reload Game (Shift + R)",
u"Variable Viewer",
u"Return to the developer menu",
u"Theme Test",
u"Style Hierarchy",
u"Image Location Picker",
u"Filename List",
u"Done",
u"Auto",
u"Quick",
u"The patterns did not match any files, so no archive was created.",
u"Archiving Files...",
u"Success",
u"The files have been added to the archive, and moved into the \"archived\" directory. Future runs of the archiver will archive files in both the \"game\" and \"archived\" directories.",
u"Archiver",
u"The archiver allows you to obfuscate your game by including files in an archive file.",
u"Archive Name:",
u"The name of the archive to create.",
u"Include Patterns:",
u"Files matching these patterns are included in the archive.",
u"Exclude Patterns:",
u"Files matching these patterns are excluded from the archive.",
u"Archive",
u"Build the archive.",
u"Cancel",
u"Archive Name",
u"The name of the archive file to create, without the .rpa extension.\n\nThe \"data\" archive is loaded automatically. Other archives must be added to config.archives.",
u"Include Patterns",
u"This is a space-separated list of file patterns. Files matching these patterns are added to the archive.\n\nAsterisks (*) can be used as a wildcard.",
u"This is a space-separated list of file patterns. Files matching these patterns are excluded from the archive. If a file is matched by both an exclude and include pattern, the exclude takes precedence.\n\nAsterisks (*) can be used as a wildcard.",
u"Scanning Files...",
u"Could not modify options.rpy. Perhaps it was changed too much.",
u"Theme changed to %s.",
u"Planetarium",
u"The options.rpy file does not exist in the game directory, so this launcher cannot change the theme.",
u"Themes control the basic look of interface elements. You'll be able to pick a color scheme next.",
u"Choose Theme",
u"Please choose a color scheme for your project.",
u"Choose Color Scheme",
u"Base Name",
u"Please enter in the base name for your distribution. This name is used to generate the names of directories and archive files. Usually, this is the name of your game, plus a version number, like \"moonlight-1.0\".",
u"Executable Name",
u"Please enter a name for the executables in your distribution. This should not include an extension, as that will be added automatically.",
u"Ignore Extensions",
u"Please enter a space-separated list of file extensions. Files with these extensions will not be included in the built distributions.",
u"Documentation Extensions",
u"Please enter a space separated list of documentation extensions. Files in the base directory with these extensions will have a second copy stored outside of the Macintosh application.",
u"Thank you for choosing Ren'Py.",
u"The distributions have been built. Be sure to test them before release.\n\nNote that unpacking and repacking the Macintosh, Linux, or Combined distributions on Windows is not supported.\n\nPlease announce your release at the {a=http://lemmasoft.renai.us/forums/}Lemma Soft Forums{/a}, and add it to {a=http://games.renpy.org}games.renpy.org{/a}.",
u"Building Distributions",
u"I've just performed a lint on your project. If it contains errors, you should say no and fix them.\nPlease also check {a=http://www.renpy.org/wiki/renpy/Download_Ren'Py}www.renpy.org{/a} to see if updates or fixes are available.\n\nDo you want to continue?",
u"Base Name:",
u"Used to generate the names of directories and archive files.",
u"Executable Name:",
u"Used to generate the names of executables and runnable programs.",
u"Ignore Extensions:",
u"Files with these extensions will not be included in the distributions.",
u"Documentation Extensions:",
u"Files with these extensions will be treated as documentation, when building the Macintosh application.",
u"Distributions to Build:",
u"Windows x86",
u"Zip distribution for the 32-bit Windows platform.",
u"Linux x86",
u"Tar.Bz2 distribution for the Linux x86 platform.",
u"Macintosh Universal",
u"Single application distribution for the Macintosh x86 and ppc platforms.",
u"Windows/Linux/Mac Combined",
u"Zip distribution for the Windows x86, Linux x86, Macintosh x86 and Macintosh ppc platforms.",
u"Build",
u"Start building the distributions.",
u"The editor has been set from the RENPY_EDITOR environment variable, and cannot be changed.",
u"Choose Editor",
u"Please choose the editor that will be use to edit scripts and display errors. More editors can be downloaded from {a=http://www.renpy.org/wiki/renpy/Editors}the Ren'Py website{/a}.",
u"Error",
u"Press enter when done.",
u"The string cannot be empty. Please enter some text.",
u"Non-ASCII filenames are not allowed. This is because Zip files cannot reliably represent non-ASCII filenames.",
u"Processed %d of %d files.",
u"Welcome!",
u"New Project",
u"Please type the name of your new project.",
u"Something with that name already exists in the projects directory.",
u"Creating Project",
u"Please wait while we create the project.",
u"Could not create the project directory. The error was: %s",
u"%s Launcher",
u"Launcher Options",
u"Using RENPY_EDITOR",
u"Change the default text editor.",
u"Projects Directory",
u"Select the directory Ren'Py searches for projects.",
u"Select Project",
u"Tutorial game has been launched.",
u"%s has been launched.",
u"Opening game directory:\n%s",
u"No editor has been selected.",
u"No files to edit.",
u"Launching the editor failed.",
u"Launched editor with %d script files.",
u"Lint",
u"Lint in progress.",
u"Lint complete.",
u"Deleting persistent data.",
u"Delete Persistent",
u"Persistent data has been deleted.",
u"Choose Projects Directory",
u"Please choose the directory containing your projects.",
u"Could not run zenity. The projects directory has been set to the directory immediately above the directory containing Ren'Py.",
u"Tutorial Game",
u"Launches the Ren'Py tutorial game.",
u"Update Ren'Py",
u"Updates Ren'Py to the latest version.",
u"Now showing the Ren'Py documentation in your web browser.",
u"Now showing the Ren'Py Games List in your web browser.",
u"Launch",
u"Launches the project.",
u"Edit Script",
u"Edits the script of the project.",
u"Game Directory",
u"Opens the project's game directory.",
u"Check Script (Lint)",
u"Checks the script of the project for likely errors.",
u"Changes the theme used by the project.",
u"Deletes the persistent data associated with the project.",
u"Archive Files",
u"Archives files found in the game and archived directories.",
u"Build Distributions",
u"Builds distributions of the project.",
u"Ren'Py",
u"Select a project to work with.",
u"Create a new project.",
u"Ren'Py Games List",
u"Visit the Ren'Py games list, at http://games.renpy.org.",
u"Causes the launcher to exit.",
u"Options",
u"Change Ren'Py launcher options.",
u"Ren'Py Help",
u"Open the Ren'Py documentation in a web browser.",
u"Retrieving catalog",
u"Checking files",
u"Making directories.",
u"Downloading updated files",
u"Renaming files",
u"Fixing permissions.",
]
-474
View File
@@ -1,474 +0,0 @@
init 1 python:
# This is a list of (theme name, theme function, theme exemplar) tuples.
themes = [
("Roundrect", "roundrect", "Basic Blue"),
("Bordered", "bordered", "Dramatic Flesh"),
("Diamond", "diamond", "Colorblind"),
("Regal", "regal", "Fine China"),
("Austen", "austen", "White Chocolate"),
("TV", "tv", "Old Polaroid"),
("3D", "threeD", "Colorblind"),
("Glow", "glow", "Really Red"),
("Marker", "marker", "Creamsicle"),
("Crayon", "crayon", "First Valentines"),
]
# This is a map from theme function to template text that sensibly calls
# that function.
theme_templates = { }
theme_templates["roundrect"] = """\
theme.roundrect(
# Color scheme: %(name)s
## The color of an idle widget face.
widget = "%(widget)s",
## The color of a focused widget face.
widget_hover = "%(widget_hover)s",
## The color of the text in a widget.
widget_text = "%(widget_text)s",
## The color of the text in a selected widget. (For
## example, the current value of a preference.)
widget_selected = "%(widget_selected)s",
## The color of a disabled widget face.
disabled = "%(disabled)s",
## The color of disabled widget text.
disabled_text = "%(disabled_text)s",
## The color of informational labels.
label = "%(label)s",
## The color of a frame containing widgets.
frame = "%(frame)s",
## The background of the main menu. This can be a color
## beginning with '#', or an image filename. The latter
## should take up the full height and width of the screen.
mm_root = "%(mm_root)s",
## The background of the game menu. This can be a color
## beginning with '#', or an image filename. The latter
## should take up the full height and width of the screen.
gm_root = "%(gm_root)s",
## If this is True, the in-game window is rounded. If False,
## the in-game window is square.
rounded_window = False,
## And we're done with the theme. The theme will customize
## various styles, so if we want to change them, we should
## do so below.
)"""
theme_templates["bordered"] = theme_templates["roundrect"].replace("roundrect", "bordered")
theme_templates["diamond"] = theme_templates["roundrect"].replace("roundrect", "diamond")
theme_templates["tv"] = theme_templates["roundrect"].replace("roundrect", "tv")
theme_templates["glow"] = theme_templates["roundrect"].replace("roundrect", "glow")
theme_templates["regal"] = theme_templates["roundrect"].replace("roundrect", "regal")
theme_templates["crayon"] = theme_templates["roundrect"].replace("roundrect", "crayon")
theme_templates["threeD"] = theme_templates["roundrect"].replace("roundrect", "threeD")
theme_templates["marker"] = theme_templates["roundrect"].replace("roundrect", "marker")
theme_templates["austen"] = theme_templates["roundrect"].replace("roundrect", "austen")
# This is a map from theme name to the code needed to implement that
# theme.
theme_data = {
'Basic Blue': {'disabled': '#404040',
'disabled_text': '#c8c8c8',
'frame': '#6496c8',
'gm_root': '#dcebff',
'label': '#ffffff',
'mm_root': '#dcebff',
'widget': '#003c78',
'widget_hover': '#0050a0',
'widget_selected': '#ffffc8',
'widget_text': '#c8ffff'},
'Bloody Mary': {'disabled': '#400000',
'disabled_text': '#260000',
'frame': '#400808',
'gm_root': '#000000',
'label': '#ffffff',
'mm_root': '#000000',
'widget': '#000000',
'widget_hover': '#830000',
'widget_selected': '#ffffff',
'widget_text': '#C2C2C2'},
'Colorblind': {'disabled': '#898989',
'disabled_text': '#666666',
'frame': '#252525',
'gm_root': '#393939',
'label': '#c2c2c2',
'mm_root': '#393939',
'widget': '#898989',
'widget_hover': '#464646',
'widget_selected': '#F2F2F2',
'widget_text': '#CCCCCC'},
'Cotton Candy': {'disabled': '#C8AFA1',
'disabled_text': '#E1D4C9',
'frame': '#FCF5F2',
'gm_root': '#D0B4BA',
'label': '#805C40',
'mm_root': '#D0B4BA',
'widget': '#ECC7D0',
'widget_hover': '#E1D4C9',
'widget_selected': '#805C40',
'widget_text': '#805C40'},
'Creamsicle': {'disabled': '#FFECBF',
'disabled_text': '#ffffff',
'frame': '#FFECBF',
'gm_root': '#FDF5E3',
'label': '#502F13',
'mm_root': '#FDF5E3',
'widget': '#D96B00',
'widget_hover': '#FD9B1C',
'widget_selected': '#ffffff',
'widget_text': '#FCE6B1'},
'Dramatic Flesh': {'disabled': '#ab6038',
'disabled_text': '#BF7C51',
'frame': '#49271b',
'gm_root': '#2a201f',
'label': '#ffffff',
'mm_root': '#2a201f',
'widget': '#BF7C51',
'widget_hover': '#dda570',
'widget_selected': '#ffffff',
'widget_text': '#E5DFDF'},
'Easter Baby': {'disabled': '#DDE9FF',
'disabled_text': '#A6AFBF',
'frame': '#CCF8DC',
'gm_root': '#FBF9DF',
'label': '#698071',
'mm_root': '#FBF9DF',
'widget': '#F5D4EE',
'widget_hover': '#F0DDFF',
'widget_selected': '#000000',
'widget_text': '#698071'},
'Favorite Jeans': {'disabled': '#919994',
'disabled_text': '#B6BFB9',
'frame': '#6f7571',
'gm_root': '#b0b8ba',
'label': '#ffffff',
'mm_root': '#b0b8ba',
'widget': '#8699a7',
'widget_hover': '#9eb1ad',
'widget_selected': '#ffffff',
'widget_text': '#dcdfd6'},
'Fine China': {'disabled': '#ADB9CC',
'disabled_text': '#DFBA14',
'frame': '#ADB9CC',
'gm_root': '#F7F7FA',
'label': '#39435E',
'mm_root': '#F7F7FA',
'widget': '#6A7183',
'widget_hover': '#1A2B47',
'widget_selected': '#E3E3E4',
'widget_text': '#C9C9CB'},
'First Valentines': {'disabled': '#F8F2D0',
'disabled_text': '#BFA1A1',
'frame': '#F8F2D0',
'gm_root': '#D98989',
'label': '#5D1010',
'mm_root': '#D98989',
'widget': '#F09898',
'widget_hover': '#D6C5BB',
'widget_selected': '#B31E1E',
'widget_text': '#593131'},
'Ice Queen': {'disabled': '#F0F2F2',
'disabled_text': '#FBFBFB',
'frame': '#ffffff',
'gm_root': '#E6E6E6',
'label': '#D9D9D9',
'mm_root': '#E6E6E6',
'widget': '#D9D9D9',
'widget_hover': '#F0F2F2',
'widget_selected': '#737373',
'widget_text': '#ffffff'},
'Mocha Latte': {'disabled': '#614D3A',
'disabled_text': '#80654D',
'frame': '#926841',
'gm_root': '#1A140E',
'label': '#F1EBE5',
'mm_root': '#1A140E',
'widget': '#4D3B29',
'widget_hover': '#996E45',
'widget_selected': '#ffffff',
'widget_text': '#B99D83'},
'Muted Horror': {'disabled': '#73735C',
'disabled_text': '#8C8C70',
'frame': '#555544',
'gm_root': '#1A0001',
'label': '#1A0001',
'mm_root': '#1A0001',
'widget': '#777777',
'widget_hover': '#73735C',
'widget_selected': '#000000',
'widget_text': '#404033'},
'Old Polaroid': {'disabled': '#A89E7D',
'disabled_text': '#CCC097',
'frame': '#49403E',
'gm_root': '#A84A3E',
'label': '#ffffff',
'mm_root': '#A84A3E',
'widget': '#A89E7D',
'widget_hover': '#8DB6B9',
'widget_selected': '#ffffff',
'widget_text': '#49403E'},
'Really Red': {'disabled': '#404040',
'disabled_text': '#c8c8c8',
'frame': '#e17373',
'gm_root': '#ffd0d0',
'label': '#ffffff',
'mm_root': '#ffd0d0',
'widget': '#963232',
'widget_hover': '#c83232',
'widget_selected': '#ffffc8',
'widget_text': '#ffffff'},
'Summer Sky': {'disabled': '#6074BF',
'disabled_text': '#7383BF',
'frame': '#6074BF',
'gm_root': '#B4CDD4',
'label': '#94C7D4',
'mm_root': '#B4CDD4',
'widget': '#F2E6AA',
'widget_hover': '#FCFCA4',
'widget_selected': '#1A5766',
'widget_text': '#7DA8B3'},
'Swamp Critter': {'disabled': '#A2521D',
'disabled_text': '#753D00',
'frame': '#797C1C',
'gm_root': '#B09D5A',
'label': '#ffffff',
'mm_root': '#B09B4F',
'widget': '#753D00',
'widget_hover': '#B19A48',
'widget_selected': '#ffffff',
'widget_text': '#CCCAC2'},
'Urban Sprawl': {'disabled': '#8F0000',
'disabled_text': '#333333',
'frame': '#8F0000',
'gm_root': '#000000',
'label': '#ffffff',
'mm_root': '#000000',
'widget': '#333333',
'widget_hover': '#000000',
'widget_selected': '#ffffff',
'widget_text': '#6C8A2F'},
'Victorian Gingerbread': {'disabled': '#7A674F',
'disabled_text': '#664F33',
'frame': '#BF8A73',
'gm_root': '#695640',
'label': '#F2EDC4',
'mm_root': '#695640',
'widget': '#7A674F',
'widget_hover': '#BDA77D',
'widget_selected': '#FDFBEE',
'widget_text': '#F2EDC4'},
'Watermelon Pie': {'disabled': '#FABF46',
'disabled_text': '#FFE06D',
'frame': '#C3CD91',
'gm_root': '#F7F7C5',
'label': '#FCFCD7',
'mm_root': '#F7F7C5',
'widget': '#FFE06D',
'widget_hover': '#E38A4F',
'widget_selected': '#996600',
'widget_text': '#FAA700'},
'White Chocolate': {'disabled': '#614D3A',
'disabled_text': '#80654D',
'frame': '#926841',
'gm_root': '#FBF9EA',
'label': '#F1EBE5',
'mm_root': '#FBF9EA',
'widget': '#33271C',
'widget_hover': '#ECE7C4',
'widget_selected': '#ffffff',
'widget_text': '#B99D83'},
'Winter Mint': {'disabled': '#426143',
'disabled_text': '#819981',
'frame': '#245536',
'gm_root': '#e5f1e5',
'label': '#ffffff',
'mm_root': '#e5f1e5',
'widget': '#7AA27B',
'widget_hover': '#A3C7A3',
'widget_selected': '#ffffff',
'widget_text': '#CDE0CE'},
'Mint Chocolate': {'disabled': '#ffe69c',
'disabled_text': '#ddbc7e',
'frame': '#8ab395',
'gm_root': '#7a4229',
'label': '#7a4229',
'mm_root': '#7a4229',
'widget': '#ffe69c',
'widget_hover': '#f5c153',
'widget_selected': '#7a4229',
'widget_text': '#b5743a'},
'Parachute Pants': {'disabled': '#53c7bb',
'disabled_text': '#97d7bd',
'frame': '#457b9f',
'gm_root': '#37397f',
'label': '#cce2ae',
'mm_root': '#37397f',
'widget': '#53c7bb',
'widget_hover': '#97d7bd',
'widget_selected': '#37397f',
'widget_text': '#457b9f'},
'Strawberry Orchard': {'disabled': '#b3c292',
'disabled_text': '#525748',
'frame': '#d8ebae',
'gm_root': '#e7f3cb',
'label': '#525748',
'mm_root': '#e7f3cb',
'widget': '#525748',
'widget_hover': '#f45c73',
'widget_selected': '#ffce95',
'widget_text': '#e7f3cb'},
'Grape Jelly': {'disabled': '#81859a',
'disabled_text': '#5e2862',
'frame': '#8ea9b0',
'gm_root': '#5e2862',
'label': '#ffffff',
'mm_root': '#5e2862',
'widget': '#c45693',
'widget_hover': '#5e2862',
'widget_selected': '#e59eae',
'widget_text': '#ffffff'},
'Dreamscape': {'disabled': '#966077',
'disabled_text': '#c75f77',
'frame': '#836177',
'gm_root': '#c75f77',
'label': '#fefab6',
'mm_root': '#c75f77',
'widget': '#77a493',
'widget_hover': '#8accb3',
'widget_selected': '#c75f77',
'widget_text': '#ffffff'},
'Unrequited Love': {'disabled': '#dbe4dd',
'disabled_text': '#bd9ca9',
'frame': '#fffeed',
'gm_root': '#b38698',
'label': '#23000e',
'mm_root': '#b38698',
'widget': '#7fa1b3',
'widget_hover': '#b38698',
'widget_selected': '#fffeed',
'widget_text': '#ffffff'},
'Watermellon': {'disabled': '#f9cdad',
'disabled_text': '#fc9d9a',
'frame': '#f9cdad',
'gm_root': '#83af9b',
'label': '#fe4365',
'mm_root': '#83af9b',
'widget': '#fc9d9a',
'widget_hover': '#fe4365',
'widget_selected': '#e5fcc2',
'widget_text': '#ffffff'},
'City Lights': {'disabled': '#638e89',
'disabled_text': '#594f4f',
'frame': '#547980',
'gm_root': '#594f4f',
'label': '#e5fcc2',
'mm_root': '#594f4f',
'widget': '#45ada8',
'widget_hover': '#2e5860',
'widget_selected': '#e5fcc2',
'widget_text': '#9de0ad'},
'Vampire Bite': {'disabled': '#971140',
'disabled_text': '#bd4b40',
'frame': '#bd1550',
'gm_root': '#490a3d',
'label': '#f8ca00',
'mm_root': '#490a3d',
'widget': '#e97f02',
'widget_hover': '#f5a240',
'widget_selected': '#490a3d',
'widget_text': '#bd1550'},
'Underground Rave': {'disabled': '#57cdff',
'disabled_text': '#717be5',
'frame': '#04b4ff',
'gm_root': '#cd249b',
'label': '#000000',
'mm_root': '#cd249b',
'widget': '#8833ce',
'widget_hover': '#cd249b',
'widget_selected': '#b6d754',
'widget_text': '#000000'},
'Tree Frog': {'disabled': '#ffffff',
'disabled_text': '#1c140d',
'frame': '#cbe86b',
'gm_root': '#ffffff',
'label': '#1c140d',
'mm_root': '#ffffff',
'widget': '#1c140d',
'widget_hover': '#86827e',
'widget_selected': '#f2e9e1',
'widget_text': '#cbe86b'},
'Sun Kissed': {'disabled': '#ffffff',
'disabled_text': '#ec4c51',
'frame': '#f0874d',
'gm_root': '#f8c821',
'label': '#ffffff',
'mm_root': '#f8c821',
'widget': '#ec4c51',
'widget_hover': '#dc454a',
'widget_selected': '#f8c821',
'widget_text': '#ffffff'},
'Vintage Faded': {'disabled': '#b17d6f',
'disabled_text': '#8c4e3d',
'frame': '#ab7464',
'gm_root': '#935844',
'label': '#f7d3c8',
'mm_root': '#935844',
'widget': '#8c4e3d',
'widget_hover': '#734032',
'widget_selected': '#fcf5ed',
'widget_text': '#e2b9ad'},
'Vintage': {'disabled': '#883e35',
'disabled_text': '#b86258',
'frame': '#b86258',
'gm_root': '#a24637',
'label': '#ffffff',
'mm_root': '#a24637',
'widget': '#6c2921',
'widget_hover': '#832f26',
'widget_selected': '#ffc7c0',
'widget_text': '#fff4eb'},
'Earth Tones': {'disabled': '#12612f',
'disabled_text': '#2c6e44',
'frame': '#00551f',
'gm_root': '#6b4a27',
'label': '#ffffff',
'mm_root': '#568153',
'widget': '#ad8c31',
'widget_hover': '#568153',
'widget_selected': '#f2edc4',
'widget_text': '#ffffff'},
'Kindergarten': {'disabled': '#1ca4b2',
'disabled_text': '#22d5b3',
'frame': '#22d5b3',
'gm_root': '#ffeca6',
'label': '#ffffff',
'mm_root': '#ffeca6',
'widget': '#1781b1',
'widget_hover': '#12678e',
'widget_selected': '#f2edc4',
'widget_text': '#fdfbee'},
'Phone Operator': {'disabled': '#929292',
'disabled_text': '#ababab',
'frame': '#d2d2d2',
'gm_root': '#59667a',
'label': '#343e4d',
'mm_root': '#59667a',
'widget': '#59667a',
'widget_hover': '#343e4d',
'widget_selected': '#bed4f6',
'widget_text': '#ffffff'},
}
-62
View File
@@ -1,62 +0,0 @@
# This file contains code for updating translations. Basically, it will
# search for a non-default file in launcher, and if that file exists, it
# will append missing translations to it.
init python:
import os
import os.path
import traceback
import codecs
def update_translations():
# Find the first file in the launcher directory that was
# not distributed with Ren'Py.
for fn in sorted(os.listdir(config.gamedir)):
if not fn.endswith(".rpy"):
continue
if fn in LAUNCHER_RPYS:
continue
break
else:
return
# Find untranslated strings.
new_translations = [ ]
for t in TRANSLATION_STRINGS:
if t not in config.translations:
new_translations.append(t)
if not new_translations:
return
# Add a block to the file with the untranslated strings.
f = codecs.open(os.path.join(config.gamedir, fn), "a", "utf-8")
print >>f
print >>f, "# New translations for", renpy.version()
print >>f, "init -1 python hide:"
print >>f, " config.translations.update({"
for t in new_translations:
print >>f
print >>f, " %r" % t
print >>f, " : %r," % t
print >>f
print >>f, " })"
f.close()
init 100 python hide:
try:
update_translations()
except:
traceback.print_exc()
-58
View File
@@ -1,58 +0,0 @@
# This is an early version of an interface for the updater. For now, we just
# assume that the user wants to update the prerelease channel, and the
# base tag.
init python:
import os
import time
version_fn = os.path.join(config.basedir, "lib", "update-version.txt")
update_allowed = os.path.exists(version_fn)
label update:
python hide:
try:
info("Checking for update...", "")
old_version = file(version_fn, "rb").read()
updater = Updater(config.basedir, 'http://update.renpy.org/renpy/updates/prerelease', [ 'base' ])
# Check the version.
version, verbose_version = updater.check_version(old_version)
if version is None:
pauseinfo("Up To Date", "Your version of Ren'Py is up to date.")
renpy.jump("top")
if not yesno("Update Available", "Would you like to update to %s?" % verbose_version):
renpy.jump("top")
# If we've made it this far, the user has decided that he wants
# to try an update.
for what, amount, limit in updater.step():
progress(what, limit, amount)
f = file(version_fn, "wb")
f.write(version)
f.close()
pauseinfo("Update complete.", "The update has finished. Ren'Py will now restart.")
if sys.platform == "win32" and sys.argv[0].lower().endswith(".exe"):
proc = subprocess.Popen([sys.argv[0], config.basedir])
else:
proc = subprocess.Popen([sys.executable, sys.argv[0], config.basedir])
renpy.quit()
except UpdateException, e:
error('The update has failed, with the error message: %s' % e.args[0])
-330
View File
@@ -1,330 +0,0 @@
init python:
import urllib
import urllib2
import bz2
import os
import hashlib
import public
class UpdateException(Exception):
"""
The exception that we return if the update fails.
"""
pass
class Updater(object):
"""
A class that attempts to update a tree of files, with a reasonable
degree of safety, security, speed, and progress reporting.
"""
def __init__(self, local, remote, tags):
"""
Create a new updater.
`local`
The name of the local directory where the files are stored.
`remote`
The remote url base.
`tags`
A list of tags to update.
"""
self.local = local
self.remote = remote
# The remote version we're upgrading to.
self.version = None
# A list of directory names.
self.directories = [ ]
# A list of file, bz2-size, adler32 tuples.
self.files = [ ]
# Xbit files.
self.xbit = [ ]
# The tags we use.
self.tags = set(tags)
def snarf(self, url):
"""
Gets the url as a string.
"""
url = self.remote + "/" + url
try:
u = urllib2.urlopen(url, None, 5)
return u.read()
except Exception, e:
raise UpdateException("Could not download %r: %r" % (url, e))
def retrieve(self, relative):
"""
Downloads the bz2-compressed file, and then decompresses it into
relative.new.
"""
bz = os.path.join(self.local, relative + ".new.bz2")
new = os.path.join(self.local, relative + ".new")
relative = urllib.quote(relative)
url = self.remote + "/" + relative + ".bz2"
try:
u = urllib2.urlopen(url, None, 5)
f = file(bz, "wb")
f.write(u.read())
f.close()
u.close()
bzf = bz2.BZ2File(bz, "rb")
f = file(new, "wb")
while True:
buf = bzf.read(1024 * 1024)
if not buf:
break
f.write(buf)
f.close()
bzf.close()
os.unlink(bz)
except Exception, e:
raise UpdateException("Could not download %r: %r" % (url, e))
def check_version(self, version):
"""
Checks to see if version is still up to date. Returns a new-version,
verbose-version string. new-version is an internal representation
of the version being downloaded, while verbose-version is a
string that can be presented to the user.
The `version` argument should be given the new-version returned
from the last successful update. new-version is None if we are
up to date.
"""
remote_version = self.snarf("version")
self.version, verbose_version = remote_version.split("\n", 1)
self.version += " " + " ".join(sorted(self.tags))
if self.version.strip() == version.strip():
return None, verbose_version
else:
return self.version, verbose_version
def verify_hash(self, relative, digest):
"""
Verifies that the file `relative` has the hash value `digest`.
"""
fn = os.path.join(self.local, relative)
if not os.path.exists(fn):
return False
hash = hashlib.sha256()
try:
f = file(fn, "rb")
while True:
data = f.read(1024 * 1024)
if not data:
break
if not "renpy.app" in fn:
if fn.endswith(".rpy") or fn.endswith(".rpym") or fn.endswith(".py") or fn.endswith(".txt"):
data = data.replace("\n", "\r\n")
data = data.replace("\r\r\n", "\r\n")
hash.update(data)
f.close()
except:
return False
return hash.hexdigest() == digest
def get_catalog(self):
"""
Downloads the catalog file from the server, checking the signature
as necessary.
"""
self.retrieve("catalog1")
catalog_fn = os.path.join(self.local, "catalog1.new")
verified = False
try:
hash = hashlib.sha256()
f = file(catalog_fn, "rb")
# Read control information.
for l in f:
if l[0] == "-":
break
hash.update(l)
l = l[:-1]
a = l.split("\t")
if a[0] == "file":
cmd, digest, size, tag, name = a
if tag in self.tags:
self.files.append((name, digest, int(size)))
elif a[0] == "dir":
cmd, tag, name = a
if tag in self.tags:
self.directories.append(name)
elif a[0] == "xbit":
cmd, name = a
self.xbit.append(name)
else:
raise UpdateException("Unknown upgrade command: %r" % l)
# Compute the unsigned message.
unsigned = int("01" + hash.hexdigest(), 16)
for l in f:
a = l[:-1].split("\t")
if a[0] != "signature":
continue
signature = int(a[1], 16)
if pow(signature, public.exponent, public.modulus) == unsigned:
verified = True
f.close()
os.unlink(catalog_fn)
except UpdateException:
raise
except Exception, e:
raise UpdateException("Could not download catalog: %r" % (e,))
if not verified:
raise UpdateException("Could not verify catalog signature.")
def step(self):
"""
This is a generator that performs the steps in the update
process. It yields a (message, complete, total) tuple for
each step, and terminates upon success.
"""
yield (u"Retrieving catalog", 0, 1)
self.get_catalog()
updated_files = [ ]
download_size = 0
for i, (name, hash, size) in enumerate(self.files):
yield (u"Checking files", i, len(self.files))
if not self.verify_hash(name, hash):
updated_files.append((name, hash, size))
download_size += size
complete_size = 0
# Todo: Make directories.
for i, dir in enumerate(self.directories):
yield (u"Making directories", i, len(self.directories))
dir = os.path.join(self.local, dir)
if not os.path.exists(dir):
try:
os.mkdir(dir)
except:
raise UpdateException("Couldn't make directory %r." % dir)
complete = 0
for name, hash, size in updated_files:
yield (u"Downloading updated files", complete, len(updated_files))
if self.verify_hash(name + ".new", hash):
complete_size += size
continue
self.retrieve(name)
if not self.verify_hash(name + ".new", hash):
raise UpdateException("Could not verify download of %r." % name)
complete_size += size
complete += 1
umask = os.umask(0)
os.umask(umask)
for i, (name, hash, size) in enumerate(updated_files):
yield (u"Renaming files", i, len(updated_files))
fn = os.path.join(self.local, name)
if os.path.exists(fn + ".old"):
os.unlink(fn + ".old")
if os.path.exists(fn):
os.rename(fn, fn + ".old")
os.rename(fn + ".new", fn)
if fn.endswith(".py") and os.path.exists(fn + 'o'):
os.unlink(fn + 'o')
for i, name in enumerate(self.xbit):
yield (u"Fixing permissions", i, len(self.xbit))
fn = os.path.join(self.local, name)
if os.path.exists(fn):
os.chmod(fn, 0777 & (~umask))
# if __name__ == "__main__":
# up = Updater("/tmp/renpy-1", "file:///tmp/renpy-6.11.0", [ "base" ])
# version, verbose_version = up.check_version(1)
# for message, done, total in up.step():
# print message, "(%d/%d)" % (done, total)
# print version
# print verbose_version
Binary file not shown.
+1783 -1052
View File
File diff suppressed because it is too large Load Diff
+9 -4
View File
@@ -23,13 +23,16 @@
def version():
return (6, 12, 0)
cdef extern from "renpy.h":
cdef extern from "pygame/pygame.h":
cdef struct SDL_RWops:
pass
void import_pygame_rwobject()
SDL_RWops* RWopsFromPython(object obj)
cdef extern from "renpy.h":
void core_init()
void save_png_core(object, SDL_RWops *, int)
@@ -136,7 +139,7 @@ def pixellate(pysrc, pydst, avgwidth, avgheight, outwidth, outheight):
# and alpha. Instead, they are the first through fourth byte of data.
# The mapping between byte and color/alpha varies from system to
# system, and needs to be determined at a higher level.
def map(pysrc, pydst, r, g, b, a):
def map(pysrc, pydst, r, g, b, a): # @ReservedAssignment
if not isinstance(pysrc, PygameSurface):
raise Exception("map requires a pygame Surface as its first argument.")
@@ -421,5 +424,7 @@ def subpixel(pysrc, pydst, xoffset, yoffset, shift):
# Be sure to update scale.py when adding something new here!
import_pygame_rwobject()
core_init()
-17
View File
@@ -1,17 +0,0 @@
#!/bin/sh
try () {
"$@" || exit -1
}
BASE=/Users/tom
. $BASE/newbuild.i386/env.sh
try ./build.sh
. $BASE/newbuild.ppc/env.sh
try ./build.sh
try chmod -R a+rX $BASE/newbuild.i386/install
try chmod -R a+rX $BASE/newbuild.ppc/install
+238 -32
View File
@@ -25,12 +25,12 @@
#include <libavutil/avstring.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
// #include "libavdevice/avdevice.h"
#include <libswscale/swscale.h>
// #include "libavcodec/audioconvert.h"
// #include "libavcodec/opt.h"
// #include "cmdutils.h"
#ifdef HAS_RESAMPLE
#include <libavutil/opt.h>
#include <libavresample/avresample.h>
#endif
#include <SDL.h>
#include <SDL_thread.h>
@@ -41,8 +41,8 @@
#undef exit
#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
#define MIN_AUDIOQ_SIZE (20 * 16 * 1024)
#define MIN_FRAMES 5
/* SDL audio buffer size, in samples. Should be small to have precise
A/V sync as SDL does not have hardware buffer fullness info. */
@@ -100,17 +100,24 @@ typedef struct VideoState {
/* samples output by the codec. we reserve more space for avsync
compensation */
#ifndef HAS_RESAMPLE
uint8_t audio_buf1[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2] __attribute__ ((aligned (16))) ;
uint8_t audio_buf2[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2] __attribute__ ((aligned (16))) ;
#else
uint8_t *audio_buf1;
#endif
uint8_t *audio_buf;
unsigned int audio_buf_size; /* in bytes */
int audio_buf_index; /* in bytes */
AVPacket audio_pkt;
AVPacket audio_pkt_temp;
// AVAudioConvert *reformat_ctx;
#ifndef HAS_RESAMPLE
ReSampleContext *reformat_ctx;
#endif
int resample_frac;
int show_audio; /* if true, display audio samples */
@@ -165,6 +172,19 @@ typedef struct VideoState {
// Should we force the display of the current video frame?
int first_frame;
#ifdef HAS_RESAMPLE
// The audio frame, and the audio resample context.
enum AVSampleFormat sdl_sample_fmt;
uint64_t sdl_channel_layout;
int sdl_channels;
int sdl_sample_rate;
enum AVSampleFormat resample_sample_fmt;
uint64_t resample_channel_layout;
int resample_sample_rate;
AVAudioResampleContext *avr;
AVFrame *frame;
#endif
} VideoState;
SDL_mutex *codec_mutex = NULL;
@@ -183,7 +203,6 @@ static int debug_mv = 0;
static int workaround_bugs = 1;
static int fast = 0;
static int genpts = 0;
static int lowres = 0;
static int idct = FF_IDCT_AUTO;
static enum AVDiscard skip_frame= AVDISCARD_DEFAULT;
static enum AVDiscard skip_idct= AVDISCARD_DEFAULT;
@@ -635,7 +654,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts)
SDL_LockMutex(is->pictq_mutex);
while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&
!is->videoq.abort_request) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
SDL_UnlockMutex(is->pictq_mutex);
@@ -660,7 +679,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts)
/* wait until the picture is allocated */
while (!vp->allocated && !is->videoq.abort_request) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
SDL_UnlockMutex(is->pictq_mutex);
@@ -770,8 +789,181 @@ static int video_thread(void *arg)
return 0;
}
#ifdef HAS_RESAMPLE
/* decode one audio frame and returns its uncompressed size */
static int audio_decode_frame(VideoState *is, double *pts_ptr)
{
AVPacket *pkt_temp = &is->audio_pkt_temp;
AVPacket *pkt = &is->audio_pkt;
AVCodecContext *dec = is->audio_st->codec;
int n, len1, data_size, got_frame;
double pts;
int new_packet = 0;
int flush_complete = 0;
for (;;) {
/* NOTE: the audio packet can contain several frames */
while (pkt_temp->size > 0 || (!pkt_temp->data && new_packet)) {
int resample_changed, audio_resample;
if (!is->frame) {
if (!(is->frame = avcodec_alloc_frame()))
return AVERROR(ENOMEM);
} else
avcodec_get_frame_defaults(is->frame);
if (flush_complete)
break;
new_packet = 0;
len1 = avcodec_decode_audio4(dec, is->frame, &got_frame, pkt_temp);
if (len1 < 0) {
/* if error, we skip the frame */
pkt_temp->size = 0;
break;
}
pkt_temp->data += len1;
pkt_temp->size -= len1;
if (!got_frame) {
/* stop sending empty packets if the decoder is finished */
if (!pkt_temp->data && dec->codec->capabilities & CODEC_CAP_DELAY)
flush_complete = 1;
continue;
}
data_size = av_samples_get_buffer_size(NULL, dec->channels,
is->frame->nb_samples,
is->frame->format, 1);
audio_resample = is->frame->format != is->sdl_sample_fmt ||
is->frame->channel_layout != is->sdl_channel_layout ||
is->frame->sample_rate != is->sdl_sample_rate;
resample_changed = is->frame->format != is->resample_sample_fmt ||
is->frame->channel_layout != is->resample_channel_layout ||
is->frame->sample_rate != is->resample_sample_rate;
if ((!is->avr && audio_resample) || resample_changed) {
int ret;
if (is->avr)
avresample_close(is->avr);
else if (audio_resample) {
is->avr = avresample_alloc_context();
if (!is->avr) {
fprintf(stderr, "error allocating AVAudioResampleContext\n");
break;
}
}
if (audio_resample) {
av_opt_set_int(is->avr, "in_channel_layout", is->frame->channel_layout, 0);
av_opt_set_int(is->avr, "in_sample_fmt", is->frame->format, 0);
av_opt_set_int(is->avr, "in_sample_rate", is->frame->sample_rate, 0);
av_opt_set_int(is->avr, "out_channel_layout", is->sdl_channel_layout, 0);
av_opt_set_int(is->avr, "out_sample_fmt", is->sdl_sample_fmt, 0);
av_opt_set_int(is->avr, "out_sample_rate", is->sdl_sample_rate, 0);
if ((ret = avresample_open(is->avr)) < 0) {
fprintf(stderr, "error initializing libavresample\n");
break;
}
}
is->resample_sample_fmt = is->frame->format;
is->resample_channel_layout = is->frame->channel_layout;
is->resample_sample_rate = is->frame->sample_rate;
}
if (audio_resample) {
void *tmp_out;
int out_samples, out_size, out_linesize;
int osize = av_get_bytes_per_sample(is->sdl_sample_fmt);
int nb_samples = is->frame->nb_samples;
int max_samples = 2 * (avresample_get_delay(is->avr) + nb_samples) * is->sdl_sample_rate / is->frame->sample_rate;
out_size = av_samples_get_buffer_size(
&out_linesize,
is->sdl_channels,
max_samples,
is->sdl_sample_fmt, 0);
tmp_out = av_realloc(is->audio_buf1, out_size);
if (!tmp_out)
return AVERROR(ENOMEM);
is->audio_buf1 = tmp_out;
out_samples = avresample_convert(is->avr,
&is->audio_buf1,
out_linesize, max_samples,
is->frame->data,
is->frame->linesize[0],
is->frame->nb_samples);
if (out_samples < 0) {
fprintf(stderr, "avresample_convert() failed\n");
break;
}
is->audio_buf = is->audio_buf1;
data_size = out_samples * osize * is->sdl_channels;
} else {
is->audio_buf = is->frame->data[0];
}
/* if no pts, then compute it */
pts = is->audio_clock;
*pts_ptr = pts;
n = is->sdl_channels * av_get_bytes_per_sample(is->sdl_sample_fmt);
is->audio_clock += (double)data_size /
(double)(n * is->sdl_sample_rate);
// This is Ren'Py specific code, to deal with ogg files with
// more data than their duration.
if (is->audio_duration) {
int len = data_size / 4;
int maxlen = is->audio_duration - is->audio_played;
if (len > maxlen) {
len = maxlen;
}
is->audio_played += len;
data_size = len * 4;
}
return data_size;
}
/* free the current packet */
if (pkt->data)
av_free_packet(pkt);
memset(pkt_temp, 0, sizeof(*pkt_temp));
if (is->paused || is->audioq.abort_request) {
return -1;
}
/* read next packet */
if ((new_packet = packet_queue_get(&is->audioq, pkt, 1)) < 0)
return -1;
if (pkt->data == flush_pkt.data) {
avcodec_flush_buffers(dec);
flush_complete = 0;
}
*pkt_temp = *pkt;
/* if update the audio clock with the pts */
if (pkt->pts != AV_NOPTS_VALUE) {
is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
}
}
}
#else
/* decode one audio frame and returns its uncompressed size */
static int audio_decode_frame(VideoState *is, double *pts_ptr)
@@ -823,17 +1015,17 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
}
// Moved by tom, from below next block. Is this right?
if (is->reformat_ctx) {
int len = data_size / av_get_bytes_per_sample(dec->sample_fmt);
len /= dec->channels;
len = audio_resample(is->reformat_ctx, (short *) is->audio_buf2, (short *) is->audio_buf1, len);
data_size = len * 4;
is->audio_buf = is->audio_buf2;
is->audio_buf = is->audio_buf2;
} else {
is->audio_buf = is->audio_buf1;
}
@@ -846,7 +1038,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
int inpos;
int len = data_size / 4;
if (is->audio_buf == is->audio_buf1) {
in = (short *) is->audio_buf1;
out = (short *) is->audio_buf2;
@@ -860,9 +1052,9 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
// which seems reasonable.
in[2 * len] = 2 * in[2 * len - 2] - in[2 * len - 4];
in[2 * len + 1] = 2 * in[2 * len - 1] - in[2 * len - 3];
// The number of bytes of input consumed per output
// sample. Scaled by 1 << 14.
// sample. Scaled by 1 << 14.
in_per_out = (dec->sample_rate << 14) / audio_sample_rate;
len *= (1 << 14);
@@ -892,14 +1084,14 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
out[2 * outpos + 1] = a + (((b - a) * frac) >> 14);
outpos++;
inpos += in_per_out;
inpos += in_per_out;
}
// Store the fraction.
is->resample_frac = inpos & ((1 << 14) - 1);
data_size = outpos * 4;
is->audio_buf = (uint8_t *) out;
is->audio_buf = (uint8_t *) out;
}
@@ -915,7 +1107,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
if (is->audio_duration) {
int len = data_size / 4;
int maxlen = is->audio_duration - is->audio_played;
if (len > maxlen) {
len = maxlen;
}
@@ -923,7 +1115,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
is->audio_played += len;
data_size = len * 4;
}
return data_size;
}
@@ -953,6 +1145,9 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
}
}
#endif
/* get the current audio output buffer size, in samples. With SDL, we
cannot have a precise information */
static int audio_write_get_buf_size(VideoState *is)
@@ -1038,8 +1233,6 @@ static int stream_component_open(VideoState *is, int stream_index)
enc->debug_mv = debug_mv;
enc->debug = debug;
enc->workaround_bugs = workaround_bugs;
enc->lowres = lowres;
if(lowres) enc->flags |= CODEC_FLAG_EMU_EDGE;
enc->idct_algo= idct;
if(fast) enc->flags2 |= CODEC_FLAG2_FAST;
enc->skip_frame= skip_frame;
@@ -1069,6 +1262,20 @@ static int stream_component_open(VideoState *is, int stream_index)
is->audio_buf_size = 0;
is->audio_buf_index = 0;
#ifdef HAS_RESAMPLE
if (!enc->channel_layout)
enc->channel_layout = av_get_default_channel_layout(enc->channels);
if (!enc->channel_layout) {
fprintf(stderr, "%s: unable to guess channel layout\n", is->filename);
return -1;
}
is->sdl_sample_rate = audio_sample_rate;
is->sdl_channel_layout = AV_CH_LAYOUT_STEREO;
is->sdl_channels = av_get_channel_layout_nb_channels(is->sdl_channel_layout);
is->sdl_sample_fmt = AV_SAMPLE_FMT_S16;
#endif
memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
packet_queue_init(&is->audioq);
break;
@@ -1102,8 +1309,10 @@ static void stream_component_close(VideoState *is, int stream_index)
case AVMEDIA_TYPE_AUDIO:
packet_queue_abort(&is->audioq);
packet_queue_end(&is->audioq);
#ifndef HAS_RESAMPLE
if (is->reformat_ctx)
audio_resample_close(is->reformat_ctx);
#endif
break;
case AVMEDIA_TYPE_VIDEO:
packet_queue_abort(&is->videoq);
@@ -1152,7 +1361,6 @@ static int decode_thread(void *arg)
AVFormatContext *ic;
int err, i, ret, video_index, audio_index;
AVPacket pkt1, *pkt = &pkt1;
AVFormatParameters params, *ap = &params;
int codecs_locked = 0;
// url_set_interrupt_cb(decode_interrupt_cb);
@@ -1162,8 +1370,6 @@ static int decode_thread(void *arg)
is->video_stream = -1;
is->audio_stream = -1;
memset(ap, 0, sizeof(*ap));
is->io_context = rwops_open(is->rwops);
codecs_locked = 1;
@@ -1293,12 +1499,12 @@ static int decode_thread(void *arg)
if (is->abort_request) {
break;
}
/* if the queue are full, no need to read more */
if (is->audioq.size > MAX_AUDIOQ_SIZE ||
is->videoq.size > MAX_VIDEOQ_SIZE) {
/* wait 2 ms - or wait for quit notify.*/
if ((is->audioq.size > MIN_AUDIOQ_SIZE || is->audio_stream < 0) &&
(is->videoq.nb_packets > MIN_FRAMES || is->video_stream < 0)) {
/* wait 2 ms - or wait for quit notify.*/
SDL_LockMutex(is->quit_mutex);
SDL_CondWaitTimeout(is->quit_cond, is->quit_mutex, 2);
SDL_UnlockMutex(is->quit_mutex);
@@ -1356,8 +1562,8 @@ fail:
if (is->video_stream >= 0)
stream_component_close(is, is->video_stream);
if (is->ic) {
av_close_input_stream(is->ic);
is->ic = NULL;
avformat_close_input(&(is->ic));
is->ic = NULL;
}
is->audio_stream = -1;
+115 -31
View File
@@ -5,37 +5,39 @@
import re
breaking = """OP CL CP QU GL NS EX SY IS PR PO NU AL ID IN HY BA BB B2 ZW CM WJ H2 H3 JL JV JT
OP ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ @ ^ ^ ^ ^ ^ ^
CL _ ^ ^ % % ^ ^ ^ ^ % % _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _
CP _ ^ ^ % % ^ ^ ^ ^ % % % % _ _ % % _ _ ^ # ^ _ _ _ _ _
QU ^ ^ ^ % % % ^ ^ ^ % % % % % % % % % % ^ # ^ % % % % %
GL % ^ ^ % % % ^ ^ ^ % % % % % % % % % % ^ # ^ % % % % %
NS _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _
EX _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _
SY _ ^ ^ % % % ^ ^ ^ _ _ % _ _ _ % % _ _ ^ # ^ _ _ _ _ _
IS _ ^ ^ % % % ^ ^ ^ _ _ % % _ _ % % _ _ ^ # ^ _ _ _ _ _
PR % ^ ^ % % % ^ ^ ^ _ _ % % % _ % % _ _ ^ # ^ % % % % %
PO % ^ ^ % % % ^ ^ ^ _ _ % % _ _ % % _ _ ^ # ^ _ _ _ _ _
NU % ^ ^ % % % ^ ^ ^ % % % % _ % % % _ _ ^ # ^ _ _ _ _ _
AL % ^ ^ % % % ^ ^ ^ _ _ % % _ % % % _ _ ^ # ^ _ _ _ _ _
ID _ ^ ^ % % % ^ ^ ^ _ % _ _ _ % % % _ _ ^ # ^ _ _ _ _ _
IN _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _
HY _ ^ ^ % _ % ^ ^ ^ _ _ % _ _ _ % % _ _ ^ # ^ _ _ _ _ _
BA _ ^ ^ % _ % ^ ^ ^ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _
BB % ^ ^ % % % ^ ^ ^ % % % % % % % % % % ^ # ^ % % % % %
B2 _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ % % _ ^ ^ # ^ _ _ _ _ _
ZW _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ^ _ _ _ _ _ _ _
CM % ^ ^ % % % ^ ^ ^ _ _ % % _ % % % _ _ ^ # ^ _ _ _ _ _
WJ % ^ ^ % % % ^ ^ ^ % % % % % % % % % % ^ # ^ % % % % %
H2 _ ^ ^ % % % ^ ^ ^ _ % _ _ _ % % % _ _ ^ # ^ _ _ _ % %
H3 _ ^ ^ % % % ^ ^ ^ _ % _ _ _ % % % _ _ ^ # ^ _ _ _ _ %
JL _ ^ ^ % % % ^ ^ ^ _ % _ _ _ % % % _ _ ^ # ^ % % % % _
JV _ ^ ^ % % % ^ ^ ^ _ % _ _ _ % % % _ _ ^ # ^ _ _ _ % %
JT _ ^ ^ % % % ^ ^ ^ _ % _ _ _ % % % _ _ ^ # ^ _ _ _ _ %
breaking = """OP CL CP QU GL NS EX SY IS PR PO NU AL HL ID IN HY BA BB B2 ZW CM WJ H2 H3 JL JV JT RI
OP ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ @ ^ ^ ^ ^ ^ ^ ^
CL _ ^ ^ % % ^ ^ ^ ^ % % _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _
CP _ ^ ^ % % ^ ^ ^ ^ % % % % % _ _ % % _ _ ^ # ^ _ _ _ _ _ _
QU ^ ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % %
GL % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % %
NS _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _
EX _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _
SY _ ^ ^ % % % ^ ^ ^ _ _ % _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _
IS _ ^ ^ % % % ^ ^ ^ _ _ % % % _ _ % % _ _ ^ # ^ _ _ _ _ _ _
PR % ^ ^ % % % ^ ^ ^ _ _ % % % % _ % % _ _ ^ # ^ % % % % % _
PO % ^ ^ % % % ^ ^ ^ _ _ % % % _ _ % % _ _ ^ # ^ _ _ _ _ _ _
NU % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _
AL % ^ ^ % % % ^ ^ ^ _ _ % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _
HL % ^ ^ % % % ^ ^ ^ _ _ % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _
ID _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _
IN _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _
HY _ ^ ^ % _ % ^ ^ ^ _ _ % _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _
BA _ ^ ^ % _ % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _
BB % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % %
B2 _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ ^ ^ # ^ _ _ _ _ _ _
ZW _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ^ _ _ _ _ _ _ _ _
CM % ^ ^ % % % ^ ^ ^ _ _ % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _
WJ % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % %
H2 _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ % % _
H3 _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ % _
JL _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ % % % % _ _
JV _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ % % _
JT _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ % _
RI _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ %
"""
other_classes = " PITCH AI BK CB CR LF NL SA SG SP XX"
other_classes = " PITCH AI BK CB CJ CR LF NL SA SG SP XX"
lines = breaking.split("\n")
@@ -45,9 +47,18 @@ print
# A map from character class to the number that represents it.
cl = { }
for i, j in enumerate((lines[0] + other_classes).split()):
print "cdef char BC_{} = {}".format(j, i)
cl[j] = i
print "CLASSES = {"
for i, j in enumerate((lines[0] + other_classes).split()):
print " \"{}\" : {},".format(j, i)
cl[j] = i
print "}"
rules = [ ]
@@ -86,7 +97,80 @@ for l in file("LineBreak.txt"):
cc[start] = m.group(2)
continue
def generate(name, func):
print "cdef char *break_classes = \"" + "".join("\\x%02x" % cl[i] for i in cc) + "\""
ncc = [ ]
for i, ccl in enumerate(cc):
ncc.append(func(i, ccl))
assert "CJ" not in ncc
assert "AI" not in ncc
print "cdef char *break_" + name + " = \"" + "".join("\\x%02x" % cl[i] for i in ncc) + "\""
def western(i, cl):
if cl == "CJ":
return "ID"
elif cl == "AI":
return "AL"
return cl
hyphens = [ 0x2010, 0x2013, 0x301c, 0x30a0 ]
iteration = [ 0x3005, 0x303B, 0x309D, 0x309E, 0x30FD, 0x30FE ]
inseperable = [ 0x2025, 0x2026 ]
centered = [ 0x003A, 0x003B, 0x30FB, 0xff1a, 0xff1b, 0xff65, 0x0021, 0x003f, 0x203c, 0x2047, 0x2048, 0x2049, 0xff01, 0xff1f ]
postfixes = [ 0x0025, 0x00A2, 0x00B0, 0x2030, 0x2032, 0x2033, 0x2103, 0xff05, 0xffe0 ]
prefixes = [ 0x0024, 0x00a3, 0x00a5, 0x20ac, 0x2116, 0xff04, 0xffe1, 0xffe5 ]
def cjk_strict(i, cl):
if cl == "CJ":
return "NS"
if cl == "AI":
return "ID"
return cl
def cjk_normal(i, cl):
if i in hyphens:
return "ID"
if cl == "CJ":
return "ID"
if cl == "AI":
return "ID"
return cl
def cjk_loose(i, cl):
if i in hyphens:
return "ID"
if i in iteration:
return "ID"
if i in inseperable:
return "ID"
if i in centered:
return "ID"
if i in postfixes:
return "ID"
if i in prefixes:
return "ID"
if cl == "CJ":
return "ID"
if cl == "AI":
return "ID"
return cl
generate("western", western)
generate("cjk_strict", cjk_strict)
generate("cjk_normal", cjk_normal)
generate("cjk_loose", cjk_loose)
+4 -1
View File
@@ -12,7 +12,7 @@
#if defined ANDROID
#define RENPY_GLES_1
#define RENPY_GLES_2
#elif defined ANGLE
@@ -54,7 +54,10 @@
#if defined RENPY_GLES_2
#ifndef ANDROID
#include <EGL/egl.h>
#endif
#include <GLES2/gl2.h>
typedef GLuint GLhandleARB;
+19 -16
View File
@@ -373,25 +373,28 @@ cdef extern from "pyfreetype.h":
FT_Error FT_Set_Char_Size(FT_Face, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt hres, FT_UInt vres)
FT_Error FT_Set_Pixel_Sizes(FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height)
ctypedef enum FT_Load_Flags:
FT_LOAD_DEFAULT
FT_LOAD_NO_SCALE
FT_LOAD_NO_HINTING
FT_LOAD_RENDER
FT_LOAD_NO_BITMAP
FT_LOAD_VERTICAL_LAYOUT
FT_LOAD_FORCE_AUTOHINT
FT_LOAD_CROP_BITMAP
FT_LOAD_PEDANTIC
FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
FT_LOAD_NO_RECURSE
FT_LOAD_IGNORE_TRANSFORM
FT_LOAD_MONOCHROME
FT_LOAD_LINEAR_DESIGN
FT_LOAD_SBITS_ONLY
FT_LOAD_NO_AUTOHINT
FT_Error FT_Load_Glyph(FT_Face face, FT_UInt glyph_index, FT_Int32 flags)
FT_Error FT_Load_Char(FT_Face face, FT_ULong char_code, FT_Int32 flags)
DEF FT_LOAD_DEFAULT = 0
DEF FT_LOAD_NO_SCALE = 1 << 0
DEF FT_LOAD_NO_HINTING = 1 << 1
DEF FT_LOAD_RENDER = 1 << 2
DEF FT_LOAD_NO_BITMAP = 1 << 3
DEF FT_LOAD_VERTICAL_LAYOUT = 1 << 4
DEF FT_LOAD_FORCE_AUTOHINT = 1 << 5
DEF FT_LOAD_CROP_BITMAP = 1 << 6
DEF FT_LOAD_PEDANTIC = 1 << 7
DEF FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH = 1 << 9
DEF FT_LOAD_NO_RECURSE = 1 << 10
DEF FT_LOAD_IGNORE_TRANSFORM = 1 << 11
DEF FT_LOAD_MONOCHROME = 1 << 12
DEF FT_LOAD_LINEAR_DESIGN = 1 << 13
DEF FT_LOAD_SBITS_ONLY = 1 << 14
DEF FT_LOAD_NO_AUTOHINT = 1 << 15
void FT_Set_Transform(FT_Face face, FT_Matrix *matrix, FT_Vector *delta)
+15
View File
@@ -3,6 +3,21 @@ cdef extern from "pygame/pygame.h":
int w
int h
int pitch
int flags
void *pixels
struct SDL_Rect:
int x
int y
int w
int h
SDL_Surface *PySurface_AsSurface(object)
int SDL_SetAlpha(SDL_Surface *surface, unsigned int flag, char alpha)
enum:
SDL_SRCALPHA
cdef extern int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect) nogil
+99
View File
@@ -0,0 +1,99 @@
from freetype cimport *
cdef extern from "stdint.h":
ctypedef signed short int16_t
ctypedef unsigned short uint16_t
ctypedef signed long int32_t
ctypedef unsigned long uint32_t
cdef extern from "ttgsubtable.h":
ctypedef struct tt_gsub_header:
uint32_t Version
uint16_t ScriptList
uint16_t FeatureList
uint16_t LookupList
ctypedef struct TLangSys:
uint16_t LookupOrder
uint16_t ReqFeatureIndex
uint16_t FeatureCount
uint16_t *FeatureIndex
ctypedef struct TLangSysRecord:
uint32_t LangSysTag
TLangSys LangSys
ctypedef struct TScript:
uint16_t DefaultLangSys
uint16_t LangSysCount
TLangSysRecord *LangSysRecord
ctypedef struct TScriptRecord:
uint32_t ScriptTag
TScript Script
ctypedef struct TScriptList:
uint16_t ScriptCount
TScriptRecord *ScriptRecord
ctypedef struct TFeature:
uint16_t FeatureParams
int LookupCount
uint16_t *LookupListIndex
ctypedef struct TFeatureRecord:
uint32_t FeatureTag
TFeature Feature
ctypedef struct TFeatureList:
int FeatureCount
TFeatureRecord *FeatureRecord
ctypedef struct TRangeRecord:
uint16_t Start
uint16_t End
uint16_t StartCoverageIndex
ctypedef struct TCoverageFormat:
uint16_t CoverageFormat
uint16_t GlyphCount
uint16_t *GlyphArray
uint16_t RangeCount
TRangeRecord *RangeRecord
ctypedef struct TSingleSubstFormat:
uint16_t SubstFormat
TCoverageFormat Coverage
int16_t DeltaGlyphID
uint16_t GlyphCount
uint16_t *Substitute
ctypedef struct TLookup:
uint16_t LookupType
uint16_t LookupFlag
uint16_t SubTableCount
TSingleSubstFormat *SubTable
ctypedef struct TLookupList:
int LookupCount
TLookup *Lookup
ctypedef struct TTGSUBTable:
int loaded
tt_gsub_header header
TScriptList ScriptList
TFeatureList FeatureList
TLookupList LookupList
ctypedef struct TTGSUBTable:
int loaded
tt_gsub_header header
TScriptList ScriptList
TFeatureList FeatureList
TLookupList LookupList
void LoadGSUBTable(TTGSUBTable *table, FT_Face face)
int GetVerticalGlyph(TTGSUBTable *table, uint32_t glyphnum, uint32_t *vglyphnum)
void init_gsubtable(TTGSUBTable *table)
void free_gsubtable(TTGSUBTable *table)
+2 -2
View File
@@ -357,7 +357,7 @@ mmx_ok(void)
#define mmx_r2m(op, reg, mem) \
__asm__ __volatile__ (#op " %%" #reg ", %0" \
: "=X" (mem) \
: "=m" (mem) \
: /* nothing */ )
#define mmx_r2r(op, regs, regd) \
@@ -394,7 +394,7 @@ mmx_ok(void)
*/
#define movd_m2r(var, reg) mmx_m2r(movd, var, reg)
#define movd_r2m(reg, var) mmx_r2m(movd, reg, var)
#define movd_r2r(regs, regd) mmx_r2r(movd, regs, regd)
#define movd_r2r(regs, regd) mmx_r2r(movd, regs, regd)
#define movd(vars, vard) \
__asm__ __volatile__ ("movd %1, %%mm0\n\t" \
"movd %%mm0, %0" \
-2
View File
@@ -27,8 +27,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <Python.h>
#include <SDL/SDL.h>
SDL_RWops* RWopsFromPythonThreaded(PyObject* obj);
void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight, int paused);
void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight);
void PSS_stop(int channel);
+8 -3
View File
@@ -20,12 +20,15 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cdef extern from "pss.h":
cdef extern from "pygame/pygame.h":
cdef struct SDL_RWops:
pass
void import_pygame_rwobject()
SDL_RWops* RWopsFromPythonThreaded(object obj)
cdef extern from "pss.h":
void PSS_play(int channel, SDL_RWops *rw, char *ext, object name, int fadein, int tight, int paused)
void PSS_queue(int channel, SDL_RWops *rw, char *ext, object name, int fadein, int tight)
void PSS_stop(int channel)
@@ -160,7 +163,7 @@ def init(freq, stereo, samples, status=False):
PSS_init(freq, stereo, samples, status)
check_error()
def quit():
def quit(): # @ReservedAssignment
PSS_quit()
def periodic():
@@ -181,3 +184,5 @@ def movie_size():
def check_version(version):
if version < 2 or version > 4:
raise Exception("pysdlsound version mismatch.")
import_pygame_rwobject()
-2
View File
@@ -4,8 +4,6 @@
#include <Python.h>
#include <SDL/SDL.h>
SDL_RWops* RWopsFromPython(PyObject* obj);
void core_init(void);
void save_png_core(PyObject *pysurf, SDL_RWops *file, int compress);
-859
View File
@@ -1,859 +0,0 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
/*
* font module for pygame
*/
#include <Python.h>
#include <stdio.h>
#include <string.h>
#include <pygame/pygame.h>
#include "structmember.h"
#include "renpy_ttf.h"
typedef struct {
PyObject_HEAD
RENPY_TTF_Font* font;
PyObject* weakreflist;
} PyFontObject;
#define PyFont_AsFont(x) (((PyFontObject*)x)->font)
static int font_initialized = 0;
static char* font_defaultname = "freesansbold.ttf";
static PyObject* self_module = NULL;
static char* pkgdatamodule_name = "pygame.pkgdata";
static char* resourcefunc_name = "getResource";
static PyObject *font_resource(char *filename) {
PyObject* load_basicfunc = NULL;
PyObject* pkgdatamodule = NULL;
PyObject* resourcefunc = NULL;
PyObject* result = NULL;
pkgdatamodule = PyImport_ImportModule(pkgdatamodule_name);
if (!pkgdatamodule) goto font_resource_end;
resourcefunc = PyObject_GetAttrString(pkgdatamodule, resourcefunc_name);
if (!resourcefunc) goto font_resource_end;
result = PyObject_CallFunction(resourcefunc, "s", filename);
if (!result) goto font_resource_end;
if (PyFile_Check(result)) {
PyObject *tmp = PyFile_Name(result);
Py_INCREF(tmp);
Py_DECREF(result);
result = tmp;
}
font_resource_end:
Py_XDECREF(pkgdatamodule);
Py_XDECREF(resourcefunc);
Py_XDECREF(load_basicfunc);
return result;
}
static void font_autoquit(void)
{
if(font_initialized)
{
font_initialized = 0;
RENPY_TTF_Quit();
}
}
static PyObject* font_autoinit(PyObject* self, PyObject* arg)
{
if(!PyArg_ParseTuple(arg, ""))
return NULL;
if(!font_initialized)
{
PyGame_RegisterQuit(font_autoquit);
if(RENPY_TTF_Init())
return PyInt_FromLong(0);
font_initialized = 1;
}
return PyInt_FromLong(font_initialized);
}
/*DOC*/ static char doc_quit[] =
/*DOC*/ "pygame.font.quit() -> none\n"
/*DOC*/ "uninitialize the font module\n"
/*DOC*/ "\n"
/*DOC*/ "Manually uninitialize SDL_ttf's font system. It is safe to call\n"
/*DOC*/ "this if font is currently not initialized.\n"
/*DOC*/ ;
static PyObject* fontmodule_quit(PyObject* self, PyObject* arg)
{
if(!PyArg_ParseTuple(arg, ""))
return NULL;
font_autoquit();
Py_RETURN_NONE;
}
/*DOC*/ static char doc_init[] =
/*DOC*/ "pygame.font.init() -> None\n"
/*DOC*/ "initialize the display module\n"
/*DOC*/ "\n"
/*DOC*/ "Manually initialize the font module. Will raise an exception if\n"
/*DOC*/ "it cannot be initialized. It is safe to call this function if\n"
/*DOC*/ "font is currently initialized.\n"
/*DOC*/ ;
static PyObject* fontmodule_init(PyObject* self, PyObject* arg)
{
PyObject* result;
int istrue;
if(!PyArg_ParseTuple(arg, ""))
return NULL;
result = font_autoinit(self, arg);
istrue = PyObject_IsTrue(result);
Py_DECREF(result);
if(!istrue)
return RAISE(PyExc_SDLError, SDL_GetError());
Py_RETURN_NONE;
}
/*DOC*/ static char doc_get_init[] =
/*DOC*/ "pygame.font.get_init() -> bool\n"
/*DOC*/ "get status of font module initialization\n"
/*DOC*/ "\n"
/*DOC*/ "Returns true if the font module is currently intialized.\n"
/*DOC*/ ;
static PyObject* get_init(PyObject* self, PyObject* arg)
{
if(!PyArg_ParseTuple(arg, ""))
return NULL;
return PyInt_FromLong(font_initialized);
}
/* font object methods */
/*DOC*/ static char doc_font_get_height[] =
/*DOC*/ "Font.get_height() -> int\n"
/*DOC*/ "average height of font glyph\n"
/*DOC*/ "\n"
/*DOC*/ "Returns the average size of each glyph in the font.\n"
/*DOC*/ ;
static PyObject* font_get_height(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(RENPY_TTF_FontHeight(font));
}
/*DOC*/ static char doc_font_get_descent[] =
/*DOC*/ "Font.get_descent() -> int\n"
/*DOC*/ "gets the font descent\n"
/*DOC*/ "\n"
/*DOC*/ "Returns the descent for the font. The descent is the number of\n"
/*DOC*/ "pixels from the font baseline to the bottom of the font.\n"
/*DOC*/ "With most fonts this is a negative number.\n"
/*DOC*/ ;
static PyObject* font_get_descent(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(RENPY_TTF_FontDescent(font));
}
/*DOC*/ static char doc_font_get_ascent[] =
/*DOC*/ "Font.get_ascent() -> int\n"
/*DOC*/ "gets the font ascent\n"
/*DOC*/ "\n"
/*DOC*/ "Returns the ascent for the font. The ascent is the number of\n"
/*DOC*/ "pixels from the font baseline to the top of the font.\n"
/*DOC*/ ;
static PyObject* font_get_ascent(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(RENPY_TTF_FontAscent(font));
}
/*DOC*/ static char doc_font_get_linesize[] =
/*DOC*/ "Font.get_linesize() -> int\n"
/*DOC*/ "gets the font recommended linesize\n"
/*DOC*/ "\n"
/*DOC*/ "Returns the linesize for the font. Each font comes with it's own\n"
/*DOC*/ "recommendation for the spacing number of pixels between each line\n"
/*DOC*/ "of the font.\n"
/*DOC*/ ;
static PyObject* font_get_linesize(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(RENPY_TTF_FontLineSkip(font));
}
/*DOC*/ static char doc_font_get_bold[] =
/*DOC*/ "Font.get_bold() -> bool\n"
/*DOC*/ "status of the bold attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Get the current status of the font's bold attribute\n"
/*DOC*/ ;
static PyObject* font_get_bold(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_BOLD) != 0);
}
/*DOC*/ static char doc_font_set_bold[] =
/*DOC*/ "Font.set_bold(bool) -> None\n"
/*DOC*/ "assign the bold attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Enables or disables the bold attribute for the font. Making the\n"
/*DOC*/ "font bold does not work as well as you expect.\n"
/*DOC*/ ;
static PyObject* font_set_bold(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int style, val;
if(!PyArg_ParseTuple(args, "i", &val))
return NULL;
style = RENPY_TTF_GetFontStyle(font);
if(val)
style |= RENPY_TTF_STYLE_BOLD;
else
style &= ~RENPY_TTF_STYLE_BOLD;
RENPY_TTF_SetFontStyle(font, style);
Py_RETURN_NONE;
}
/*DOC*/ static char doc_font_get_italic[] =
/*DOC*/ "Font.get_italic() -> bool\n"
/*DOC*/ "status of the italic attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Get the current status of the font's italic attribute\n"
/*DOC*/ ;
static PyObject* font_get_italic(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_ITALIC) != 0);
}
/*DOC*/ static char doc_font_set_italic[] =
/*DOC*/ "Font.set_italic(bool) -> None\n"
/*DOC*/ "assign the italic attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Enables or disables the italic attribute for the font.\n"
/*DOC*/ ;
static PyObject* font_set_italic(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int style, val;
if(!PyArg_ParseTuple(args, "i", &val))
return NULL;
style = RENPY_TTF_GetFontStyle(font);
if(val)
style |= RENPY_TTF_STYLE_ITALIC;
else
style &= ~RENPY_TTF_STYLE_ITALIC;
RENPY_TTF_SetFontStyle(font, style);
Py_RETURN_NONE;
}
/*DOC*/ static char doc_font_get_underline[] =
/*DOC*/ "Font.get_underline() -> bool\n"
/*DOC*/ "status of the underline attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Get the current status of the font's underline attribute\n"
/*DOC*/ ;
static PyObject* font_get_underline(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_UNDERLINE) != 0);
}
/*DOC*/ static char doc_font_set_underline[] =
/*DOC*/ "Font.set_underline(bool) -> None\n"
/*DOC*/ "assign the underline attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Enables or disables the underline attribute for the font.\n"
/*DOC*/ ;
static PyObject* font_set_underline(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int style, val;
if(!PyArg_ParseTuple(args, "i", &val))
return NULL;
style = RENPY_TTF_GetFontStyle(font);
if(val)
style |= RENPY_TTF_STYLE_UNDERLINE;
else
style &= ~RENPY_TTF_STYLE_UNDERLINE;
RENPY_TTF_SetFontStyle(font, style);
Py_RETURN_NONE;
}
/*DOC*/ static char doc_font_render[] =
/*DOC*/ "Font.render(text, antialias, fore_RGBA, [back_RGBA]) -> Surface\n"
/*DOC*/ "render text to a new image\n"
/*DOC*/ "\n"
/*DOC*/ "Render the given text onto a new image surface. The given text\n"
/*DOC*/ "can be standard python text or unicode. Antialiasing will smooth\n"
/*DOC*/ "the edges of the font for a much cleaner look. The foreground\n"
/*DOC*/ "and background color are both RGBA, the alpha component is ignored\n"
/*DOC*/ "if given. If the background color is omitted, the text will have a\n"
/*DOC*/ "transparent background.\n"
/*DOC*/ "\n"
/*DOC*/ "Note that font rendering is not thread safe, therefore only one\n"
/*DOC*/ "thread can render text at any given time.\n"
/*DOC*/ "\n"
/*DOC*/ "Also, rendering smooth text with underlines will crash with SDL_ttf\n"
/*DOC*/ "less that version 2.0, be careful.\n"
/*DOC*/ "\n"
/*DOC*/ "If you pass an empty string, render() will return a blank surface\n"
/*DOC*/ "1 pixel wide and the same height as the font.\n"
/*DOC*/ ;
static PyObject* font_render(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int aa;
PyObject* text, *final;
PyObject* fg_rgba_obj, *bg_rgba_obj = NULL;
Uint8 rgba[4];
SDL_Surface* surf;
SDL_Color foreg, backg;
if(!PyArg_ParseTuple(args, "OiO|O", &text, &aa, &fg_rgba_obj, &bg_rgba_obj))
return NULL;
if(!RGBAFromObj(fg_rgba_obj, rgba))
return RAISE(PyExc_TypeError, "Invalid foreground RGBA argument");
foreg.r = rgba[0]; foreg.g = rgba[1]; foreg.b = rgba[2];
if(bg_rgba_obj)
{
if(!RGBAFromObj(bg_rgba_obj, rgba))
return RAISE(PyExc_TypeError, "Invalid background RGBA argument");
backg.r = rgba[0];
backg.g = rgba[1];
backg.b = rgba[2];
backg.unused = 0;
} else {
backg.r = 0;
backg.g = 0;
backg.b = 0;
backg.unused = 0;
}
if(!PyObject_IsTrue(text))
{
int height = RENPY_TTF_FontHeight(font);
surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, height, 32, 0xff<<16, 0xff<<8, 0xff, 0);
if(bg_rgba_obj)
{
Uint32 c = SDL_MapRGB(surf->format, backg.r, backg.g, backg.b);
SDL_FillRect(surf, NULL, c);
}
else
SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0);
}
else if(PyUnicode_Check(text))
{
PyObject* strob = PyEval_CallMethod(text, "encode", "(s)", "utf-8");
char *string = PyString_AsString(strob);
if(aa)
{
if(!bg_rgba_obj)
surf = RENPY_TTF_RenderUTF8_Blended(font, string, foreg);
else
surf = RENPY_TTF_RenderUTF8_Shaded(font, string, foreg, backg);
}
else
surf = RENPY_TTF_RenderUTF8_Solid(font, string, foreg);
Py_DECREF(strob);
}
else if(PyString_Check(text))
{
char* string = PyString_AsString(text);
if(aa)
{
if(!bg_rgba_obj)
surf = RENPY_TTF_RenderText_Blended(font, string, foreg);
else
surf = RENPY_TTF_RenderText_Shaded(font, string, foreg, backg);
}
else
surf = RENPY_TTF_RenderText_Solid(font, string, foreg);
}
else
return RAISE(PyExc_TypeError, "text must be a string or unicode");
if(!surf)
return RAISE(PyExc_SDLError, "SDL_ttf render failed");
if(!aa && bg_rgba_obj) /*turn off transparancy*/
{
SDL_SetColorKey(surf, 0, 0);
surf->format->palette->colors[0].r = backg.r;
surf->format->palette->colors[0].g = backg.g;
surf->format->palette->colors[0].b = backg.b;
}
final = PySurface_New(surf);
if(!final)
SDL_FreeSurface(surf);
return final;
}
/*DOC*/ static char doc_font_size[] =
/*DOC*/ "Font.size(text) -> width, height\n"
/*DOC*/ "size of rendered text\n"
/*DOC*/ "\n"
/*DOC*/ "Computes the rendered size of the given text. The text can be\n"
/*DOC*/ "standard python text or unicode. Changing the bold and italic\n"
/*DOC*/ "attributes can change the size of the rendered text.\n"
/*DOC*/ ;
static PyObject* font_size(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int w, h;
PyObject* text;
if(!PyArg_ParseTuple(args, "O", &text))
return NULL;
if(PyUnicode_Check(text))
{
PyObject* strob = PyEval_CallMethod(text, "encode", "(s)", "utf-8");
char *string = PyString_AsString(strob);
RENPY_TTF_SizeUTF8(font, string, &w, &h);
Py_DECREF(strob);
}
else if(PyString_Check(text))
{
char* string = PyString_AsString(text);
RENPY_TTF_SizeText(font, string, &w, &h);
}
else
return RAISE(PyExc_TypeError, "text must be a string or unicode");
return Py_BuildValue("(ii)", w, h);
}
static PyObject* font_set_expand(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
float expand;
if(!PyArg_ParseTuple(args, "f", &expand))
return NULL;
RENPY_TTF_SetExpand(font, expand);
Py_RETURN_NONE;
}
static PyMethodDef font_methods[] =
{
{ "get_height", font_get_height, 1, doc_font_get_height },
{ "get_descent", font_get_descent, 1, doc_font_get_descent },
{ "get_ascent", font_get_ascent, 1, doc_font_get_ascent },
{ "get_linesize", font_get_linesize, 1, doc_font_get_linesize },
{ "get_bold", font_get_bold, 1, doc_font_get_bold },
{ "set_bold", font_set_bold, 1, doc_font_set_bold },
{ "get_italic", font_get_italic, 1, doc_font_get_italic },
{ "set_italic", font_set_italic, 1, doc_font_set_italic },
{ "get_underline", font_get_underline, 1, doc_font_get_underline },
{ "set_underline", font_set_underline, 1, doc_font_set_underline },
{ "render", font_render, 1, doc_font_render },
{ "size", font_size, 1, doc_font_size },
{ "set_expand", font_set_expand, 1, ""},
{ NULL, NULL }
};
/*font object internals*/
static void font_dealloc(PyFontObject* self)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(font && font_initialized)
RENPY_TTF_CloseFont(font);
if(self->weakreflist)
PyObject_ClearWeakRefs((PyObject*)self);
self->ob_type->tp_free((PyObject*)self);
}
static int font_init(PyFontObject *self, PyObject *args, PyObject *kwds)
{
int index = 0;
int fontsize;
RENPY_TTF_Font* font = NULL;
PyObject* fileobj;
self->font = NULL;
if(!PyArg_ParseTuple(args, "Oi|i", &fileobj, &fontsize, &index))
return -1;
if(!font_initialized)
{
RAISE(PyExc_SDLError, "font not initialized");
return -1;
}
Py_INCREF(fileobj);
if(fontsize <= 1)
fontsize = 1;
if(fileobj == Py_None) {
Py_DECREF(fileobj);
fileobj = font_resource(font_defaultname);
if(!fileobj)
{
RAISE(PyExc_RuntimeError, "default font not found");
return -1;
}
fontsize = (int)(fontsize * .6875);
if(fontsize <= 1)
fontsize = 1;
}
if(PyString_Check(fileobj) || PyUnicode_Check(fileobj))
{
FILE* test;
char* filename = PyString_AsString(fileobj);
Py_DECREF(fileobj);
fileobj = NULL;
if(!filename)
return -1;
/*check if it is a valid file, else SDL_ttf segfaults*/
test = fopen(filename, "rb");
if(!test)
{
if (!strcmp(filename, font_defaultname))
{
fileobj = font_resource(font_defaultname);
}
if (!fileobj) {
PyErr_SetString(PyExc_IOError, "unable to read font filename");
return -1;
}
}
else
{
fclose(test);
// Py_BEGIN_ALLOW_THREADS
font = RENPY_TTF_OpenFont(filename, fontsize);
// Py_END_ALLOW_THREADS
}
}
if (!font)
{
#ifdef RENPY_TTF_MAJOR_VERSION
SDL_RWops *rw;
rw = RWopsFromPython(fileobj);
if (!rw) {
Py_DECREF(fileobj);
return -1;
}
// Py_BEGIN_ALLOW_THREADS
font = RENPY_TTF_OpenFontIndexRW(rw, 1, fontsize, index);
// Py_END_ALLOW_THREADS
#else
Py_DECREF(fileobj);
RAISE(PyExc_NotImplementedError, "nonstring fonts require SDL_ttf-2.0.6");
return -1;
#endif
}
if(!font)
{
RAISE(PyExc_RuntimeError, SDL_GetError());
return -1;
}
self->font = font;
return 0;
}
/*DOC*/ static char doc_Font_MODULE[] =
/*DOC*/ "The font object is created only from pygame.font.Font(). Once a\n"
/*DOC*/ "font is created it's size and TTF file cannot be changed. The\n"
/*DOC*/ "Font objects are mainly used to render() text into a new Surface.\n"
/*DOC*/ "The Font objects also have a few states that can be set with\n"
/*DOC*/ "set_underline(bool), set_bold(bool), set_italic(bool). Each of\n"
/*DOC*/ "these functions contains an equivalent get_XXX() routine to find\n"
/*DOC*/ "the current state. There are also many routines to query the\n"
/*DOC*/ "dimensions of the text. The rendering functions work with both\n"
/*DOC*/ "normal python strings, as well as with unicode strings.\n"
/*DOC*/ ;
static PyTypeObject PyFont_Type =
{
PyObject_HEAD_INIT(NULL)
0,
"pygame.font.Font",
sizeof(PyFontObject),
0,
(destructor)font_dealloc,
0,
0, /*getattr*/
0,
0,
0,
0,
NULL,
0,
(hashfunc)NULL,
(ternaryfunc)NULL,
(reprfunc)NULL,
0L,0L,0L,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
doc_Font_MODULE, /* Documentation string */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
offsetof(PyFontObject, weakreflist), /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
font_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)font_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
//PyType_GenericNew, /* tp_new */
/*font module methods*/
/*DOC*/ static char doc_get_default_font[] =
/*DOC*/ "pygame.font.get_default_font() -> string\n"
/*DOC*/ "get the name of the default font\n"
/*DOC*/ "\n"
/*DOC*/ "returns the filename for the default truetype font.\n"
/*DOC*/ ;
static PyObject* get_default_font(PyObject* self, PyObject* args)
{
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyString_FromString(font_defaultname);
}
/*font module methods*/
#if 0
/*DOC*/ static char doc_Font[] =
/*DOC*/ "pygame.font.Font(file, size) -> Font\n"
/*DOC*/ "create a new font object\n"
/*DOC*/ "\n"
/*DOC*/ "This will create a new font object. The given file can\n"
/*DOC*/ "be a filename or any python file-like object.\n"
/*DOC*/ "The size represents the height of the font in\n"
/*DOC*/ "pixels. The file argument can be 'None', which will\n"
/*DOC*/ "use a plain default font.\n"
/*DOC*/ "\n"
/*DOC*/ "You must have at least SDL_ttf-2.0.6 for file object\n"
/*DOC*/ "support. You can load TTF and FON fonts.\n"
/*DOC*/ ;
#endif
static PyMethodDef font_builtins[] =
{
{ "__PYGAMEinit__", font_autoinit, 1, doc_init },
{ "init", fontmodule_init, 1, doc_init },
{ "quit", fontmodule_quit, 1, doc_quit },
{ "get_init", get_init, 1, doc_get_init },
{ "get_default_font", get_default_font, 1, doc_get_default_font },
{ NULL, NULL }
};
static PyObject* PyFont_New(RENPY_TTF_Font* font)
{
PyFontObject* fontobj;
if(!font)
return RAISE(PyExc_RuntimeError, "unable to load font.");
fontobj = (PyFontObject *)PyFont_Type.tp_new(&PyFont_Type, NULL, NULL);
if(fontobj)
fontobj->font = font;
return (PyObject*)fontobj;
}
/*DOC*/ static char doc_pygame_font_MODULE[] =
/*DOC*/ "The font module allows for rendering TrueType fonts into a new\n"
/*DOC*/ "Surface object. This module is optional and requires SDL_ttf as a\n"
/*DOC*/ "dependency. You may want to check for pygame.font to import and\n"
/*DOC*/ "initialize before attempting to use the module.\n"
/*DOC*/ "\n"
/*DOC*/ "Most of the work done with fonts are done by using the actual\n"
/*DOC*/ "Font objects. The module by itself only has routines to\n"
/*DOC*/ "initialize the module and create Font objects with\n"
/*DOC*/ "pygame.font.Font().\n"
/*DOC*/ "\n"
/*DOC*/ "You can load fonts from the standard system fonts by using the\n"
/*DOC*/ "pygame.font.SysFont() method. There are also other functions to\n"
/*DOC*/ "help you work with system fonts.\n"
/*DOC*/ "\n"
/*DOC*/ ;
PYGAME_EXPORT
void init_renpy_font(void)
{
PyObject *module;
if (PyType_Ready(&PyFont_Type) < 0)
return;
/* create the module */
PyFont_Type.ob_type = &PyType_Type;
PyFont_Type.tp_new = &PyType_GenericNew;
module = Py_InitModule3("_renpy_font", font_builtins, doc_pygame_font_MODULE);
self_module = module;
Py_INCREF((PyObject*)&PyFont_Type);
PyModule_AddObject(module, "FontType", (PyObject *)&PyFont_Type);
Py_INCREF((PyObject*)&PyFont_Type);
PyModule_AddObject(module, "Font", (PyObject *)&PyFont_Type);
import_pygame_base();
import_pygame_surface();
import_pygame_rwobject();
}
-1796
View File
File diff suppressed because it is too large Load Diff
-236
View File
@@ -1,236 +0,0 @@
/*
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
Copyright (C) 1997-2004 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
/* $Id: SDL_ttf.h 2387 2006-05-11 09:03:37Z slouken $ */
/* This library is a wrapper around the excellent FreeType 2.0 library,
available at:
http://www.freetype.org/
*/
#ifndef _RENPY_TTF_H
#define _RENPY_TTF_H
#include "SDL.h"
#include "begin_code.h"
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
*/
#define RENPY_RENPY_TTF_MAJOR_VERSION 2
#define RENPY_RENPY_TTF_MINOR_VERSION 0
#define RENPY_RENPY_TTF_PATCHLEVEL 8
/* This macro can be used to fill a version structure with the compile-time
* version of the SDL_ttf library.
*/
#define RENPY_RENPY_TTF_VERSION(X) \
{ \
(X)->major = RENPY_RENPY_TTF_MAJOR_VERSION; \
(X)->minor = RENPY_RENPY_TTF_MINOR_VERSION; \
(X)->patch = RENPY_RENPY_TTF_PATCHLEVEL; \
}
/* Backwards compatibility */
#define RENPY_TTF_MAJOR_VERSION RENPY_RENPY_TTF_MAJOR_VERSION
#define RENPY_TTF_MINOR_VERSION RENPY_RENPY_TTF_MINOR_VERSION
#define RENPY_TTF_PATCHLEVEL RENPY_RENPY_TTF_PATCHLEVEL
#define RENPY_TTF_VERSION(X) RENPY_RENPY_TTF_VERSION(X)
/* This function gets the version of the dynamically linked SDL_ttf library.
it should NOT be used to fill a version structure, instead you should
use the RENPY_RENPY_TTF_VERSION() macro.
*/
extern DECLSPEC const SDL_version * SDLCALL RENPY_TTF_Linked_Version(void);
/* ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark) */
#define UNICODE_BOM_NATIVE 0xFEFF
#define UNICODE_BOM_SWAPPED 0xFFFE
/* This function tells the library whether UNICODE text is generally
byteswapped. A UNICODE BOM character in a string will override
this setting for the remainder of that string.
*/
extern DECLSPEC void SDLCALL RENPY_TTF_ByteSwappedUNICODE(int swapped);
/* The internal structure containing font information */
typedef struct _RENPY_TTF_Font RENPY_TTF_Font;
/* Initialize the TTF engine - returns 0 if successful, -1 on error */
extern DECLSPEC int SDLCALL RENPY_TTF_Init(void);
/* Open a font file and create a font of the specified point size.
* Some .fon fonts will have several sizes embedded in the file, so the
* point size becomes the index of choosing which size. If the value
* is too high, the last indexed size will be the default. */
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFont(const char *file, int ptsize);
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontIndex(const char *file, int ptsize, long index);
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize);
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index);
/* Set and retrieve the font style
This font style is implemented by modifying the font glyphs, and
doesn't reflect any inherent properties of the truetype font file.
*/
#define RENPY_TTF_STYLE_NORMAL 0x00
#define RENPY_TTF_STYLE_BOLD 0x01
#define RENPY_TTF_STYLE_ITALIC 0x02
#define RENPY_TTF_STYLE_UNDERLINE 0x04
extern DECLSPEC int SDLCALL RENPY_TTF_GetFontStyle(RENPY_TTF_Font *font);
extern DECLSPEC void SDLCALL RENPY_TTF_SetFontStyle(RENPY_TTF_Font *font, int style);
/* Get the total height of the font - usually equal to point size */
extern DECLSPEC int SDLCALL RENPY_TTF_FontHeight(RENPY_TTF_Font *font);
/* Get the offset from the baseline to the top of the font
This is a positive value, relative to the baseline.
*/
extern DECLSPEC int SDLCALL RENPY_TTF_FontAscent(RENPY_TTF_Font *font);
/* Get the offset from the baseline to the bottom of the font
This is a negative value, relative to the baseline.
*/
extern DECLSPEC int SDLCALL RENPY_TTF_FontDescent(RENPY_TTF_Font *font);
/* Get the recommended spacing between lines of text for this font */
extern DECLSPEC int SDLCALL RENPY_TTF_FontLineSkip(RENPY_TTF_Font *font);
/* Get the number of faces of the font */
extern DECLSPEC long SDLCALL RENPY_TTF_FontFaces(RENPY_TTF_Font *font);
/* Get the font face attributes, if any */
extern DECLSPEC int SDLCALL RENPY_TTF_FontFaceIsFixedWidth(RENPY_TTF_Font *font);
extern DECLSPEC char * SDLCALL RENPY_TTF_FontFaceFamilyName(RENPY_TTF_Font *font);
extern DECLSPEC char * SDLCALL RENPY_TTF_FontFaceStyleName(RENPY_TTF_Font *font);
/* Get the metrics (dimensions) of a glyph
To understand what these metrics mean, here is a useful link:
http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
*/
extern DECLSPEC int SDLCALL RENPY_TTF_GlyphMetrics(RENPY_TTF_Font *font, Uint16 ch,
int *minx, int *maxx,
int *miny, int *maxy, int *advance);
/* Get the dimensions of a rendered string of text */
extern DECLSPEC int SDLCALL RENPY_TTF_SizeText(RENPY_TTF_Font *font, const char *text, int *w, int *h);
extern DECLSPEC int SDLCALL RENPY_TTF_SizeUTF8(RENPY_TTF_Font *font, const char *text, int *w, int *h);
extern DECLSPEC int SDLCALL RENPY_TTF_SizeUNICODE(RENPY_TTF_Font *font, const Uint16 *text, int *w, int *h);
/* Create an 8-bit palettized surface and render the given text at
fast quality with the given font and color. The 0 pixel is the
colorkey, giving a transparent background, and the 1 pixel is set
to the text color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Solid(RENPY_TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Solid(RENPY_TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Solid(RENPY_TTF_Font *font,
const Uint16 *text, SDL_Color fg);
/* Create an 8-bit palettized surface and render the given glyph at
fast quality with the given font and color. The 0 pixel is the
colorkey, giving a transparent background, and the 1 pixel is set
to the text color. The glyph is rendered without any padding or
centering in the X direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Solid(RENPY_TTF_Font *font,
Uint16 ch, SDL_Color fg);
/* Create an 8-bit palettized surface and render the given text at
high quality with the given font and colors. The 0 pixel is background,
while other pixels have varying degrees of the foreground color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Shaded(RENPY_TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Shaded(RENPY_TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Shaded(RENPY_TTF_Font *font,
const Uint16 *text, SDL_Color fg, SDL_Color bg);
/* Create an 8-bit palettized surface and render the given glyph at
high quality with the given font and colors. The 0 pixel is background,
while other pixels have varying degrees of the foreground color.
The glyph is rendered without any padding or centering in the X
direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Shaded(RENPY_TTF_Font *font,
Uint16 ch, SDL_Color fg, SDL_Color bg);
/* Create a 32-bit ARGB surface and render the given text at high quality,
using alpha blending to dither the font with the given color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Blended(RENPY_TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Blended(RENPY_TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Blended(RENPY_TTF_Font *font,
const Uint16 *text, SDL_Color fg);
/* Create a 32-bit ARGB surface and render the given glyph at high quality,
using alpha blending to dither the font with the given color.
The glyph is rendered without any padding or centering in the X
direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Blended(RENPY_TTF_Font *font,
Uint16 ch, SDL_Color fg);
/* For compatibility with previous versions, here are the old functions */
#define RENPY_TTF_RenderText(font, text, fg, bg) \
RENPY_TTF_RenderText_Shaded(font, text, fg, bg)
#define RENPY_TTF_RenderUTF8(font, text, fg, bg) \
RENPY_TTF_RenderUTF8_Shaded(font, text, fg, bg)
#define RENPY_TTF_RenderUNICODE(font, text, fg, bg) \
RENPY_TTF_RenderUNICODE_Shaded(font, text, fg, bg)
/* Close an opened font file */
extern DECLSPEC void SDLCALL RENPY_TTF_CloseFont(RENPY_TTF_Font *font);
/* De-initialize the TTF engine */
extern DECLSPEC void SDLCALL RENPY_TTF_Quit(void);
/* Check if the TTF engine is initialized */
extern DECLSPEC int SDLCALL RENPY_TTF_WasInit(void);
extern DECLSPEC void SDLCALL RENPY_TTF_SetExpand(RENPY_TTF_Font *, float);
/* We'll use SDL for reporting errors */
#define RENPY_TTF_SetError SDL_SetError
#define RENPY_TTF_GetError SDL_GetError
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include "close_code.h"
#endif /* _RENPY_TTF_H */
-451
View File
@@ -1,451 +0,0 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
/*
* Updated by Tom to separate from pygame.
*/
#define RAISE(x,y) (PyErr_SetString((x), (y)), (PyObject*)NULL)
/*
* SDL_RWops support for python objects
*/
#include <Python.h>
#include <SDL/SDL.h>
typedef struct
{
PyObject* read;
PyObject* write;
PyObject* seek;
PyObject* tell;
PyObject* close;
#ifdef WITH_THREAD
PyThreadState* thread;
#endif
}RWHelper;
static int rw_seek(SDL_RWops* context, int offset, int whence);
static int rw_read(SDL_RWops* context, void* ptr, int size, int maxnum);
static int rw_write(SDL_RWops* context, const void* ptr, int size, int maxnum);
static int rw_close(SDL_RWops* context);
#ifdef WITH_THREAD
static int rw_seek_th(SDL_RWops* context, int offset, int whence);
static int rw_read_th(SDL_RWops* context, void* ptr, int size, int maxnum);
static int rw_write_th(SDL_RWops* context, const void* ptr, int size, int maxnum);
static int rw_close_th(SDL_RWops* context);
#endif
static SDL_RWops* get_standard_rwop(PyObject* obj)
{
if(PyString_Check(obj) || PyUnicode_Check(obj))
{
int result;
char* name;
PyObject* tuple = PyTuple_New(1);
PyTuple_SET_ITEM(tuple, 0, obj);
Py_INCREF(obj);
if(!tuple) return NULL;
result = PyArg_ParseTuple(tuple, "s", &name);
Py_DECREF(tuple);
if(!result)
return NULL;
return SDL_RWFromFile(name, "rb");
}
// else if(PyFile_Check(obj))
// return SDL_RWFromFP(PyFile_AsFile(obj), 0);
return NULL;
}
static void fetch_object_methods(RWHelper* helper, PyObject* obj)
{
helper->read = helper->write = helper->seek =
helper->tell = helper->close = NULL;
if(PyObject_HasAttrString(obj, "read"))
{
helper->read = PyObject_GetAttrString(obj, "read");
if(helper->read && !PyCallable_Check(helper->read))
helper->read = NULL;
}
if(PyObject_HasAttrString(obj, "write"))
{
helper->write = PyObject_GetAttrString(obj, "write");
if(helper->write && !PyCallable_Check(helper->write))
helper->write = NULL;
}
if(PyObject_HasAttrString(obj, "seek"))
{
helper->seek = PyObject_GetAttrString(obj, "seek");
if(helper->seek && !PyCallable_Check(helper->seek))
helper->seek = NULL;
}
if(PyObject_HasAttrString(obj, "tell"))
{
helper->tell = PyObject_GetAttrString(obj, "tell");
if(helper->tell && !PyCallable_Check(helper->tell))
helper->tell = NULL;
}
if(PyObject_HasAttrString(obj, "close"))
{
helper->close = PyObject_GetAttrString(obj, "close");
if(helper->close && !PyCallable_Check(helper->close))
helper->close = NULL;
}
}
SDL_RWops* RWopsFromPython(PyObject* obj)
{
SDL_RWops* rw;
RWHelper* helper;
if(!obj)
return (SDL_RWops*) RAISE(PyExc_TypeError, "Invalid filetype object");
rw = get_standard_rwop(obj);
if(rw) return rw;
helper = PyMem_New(RWHelper, 1);
fetch_object_methods(helper, obj);
rw = SDL_AllocRW();
rw->hidden.unknown.data1 = (void*)helper;
rw->seek = rw_seek;
rw->read = rw_read;
rw->write = rw_write;
rw->close = rw_close;
return rw;
}
static int RWopsCheckPython(SDL_RWops* rw)
{
return rw->close == rw_close;
}
static int rw_seek(SDL_RWops* context, int offset, int whence)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval;
if(!helper->seek || !helper->tell)
return -1;
if(!(offset == 0 && whence == SEEK_CUR)) /*being called only for 'tell'*/
{
result = PyObject_CallFunction(helper->seek, "ii", offset, whence);
if(!result)
return -1;
Py_DECREF(result);
}
result = PyObject_CallFunction(helper->tell, NULL);
if(!result)
return -1;
retval = PyInt_AsLong(result);
Py_DECREF(result);
return retval;
}
static int rw_read(SDL_RWops* context, void* ptr, int size, int maxnum)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval;
if(!helper->read)
return -1;
result = PyObject_CallFunction(helper->read, "i", size * maxnum);
if(!result)
return -1;
if(!PyString_Check(result))
{
Py_DECREF(result);
return -1;
}
retval = PyString_GET_SIZE(result);
memcpy(ptr, PyString_AsString(result), retval);
retval /= size;
Py_DECREF(result);
return retval;
}
static int rw_write(SDL_RWops* context, const void* ptr, int size, int num)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
if(!helper->write)
return -1;
result = PyObject_CallFunction(helper->write, "s#", ptr, size * num);
if(!result)
return -1;
Py_DECREF(result);
return num;
}
static int rw_close(SDL_RWops* context)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval = 0;
if(helper->close)
{
result = PyObject_CallFunction(helper->close, NULL);
if(result)
retval = -1;
Py_XDECREF(result);
}
Py_XDECREF(helper->seek);
Py_XDECREF(helper->tell);
Py_XDECREF(helper->write);
Py_XDECREF(helper->read);
Py_XDECREF(helper->close);
PyMem_Del(helper);
SDL_FreeRW(context);
return retval;
}
SDL_RWops* RWopsFromPythonThreaded(PyObject* obj)
{
SDL_RWops* rw;
RWHelper* helper;
PyInterpreterState* interp;
PyThreadState* thread;
if(!obj)
return (SDL_RWops*)RAISE(PyExc_TypeError, "Invalid filetype object");
rw = get_standard_rwop(obj);
if(rw)
return rw;
#ifndef WITH_THREAD
return (SDL_RWops*)RAISE(PyExc_NotImplementedError, "Python built without thread support");
#else
helper = PyMem_New(RWHelper, 1);
fetch_object_methods(helper, obj);
rw = SDL_AllocRW();
rw->hidden.unknown.data1 = (void*)helper;
rw->seek = rw_seek_th;
rw->read = rw_read_th;
rw->write = rw_write_th;
rw->close = rw_close_th;
PyEval_InitThreads();
thread = PyThreadState_Get();
interp = thread->interp;
helper->thread = PyThreadState_New(interp);
return rw;
#endif
}
static int RWopsCheckPythonThreaded(SDL_RWops* rw)
{
#ifdef WITH_THREAD
return rw->close == rw_close_th;
#else
return 0;
#endif
}
#ifdef WITH_THREAD
static int rw_seek_th(SDL_RWops* context, int offset, int whence)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval;
PyThreadState* oldstate;
if(!helper->seek || !helper->tell)
return -1;
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(helper->thread);
if(!(offset == 0 && whence == SEEK_CUR)) /*being called only for 'tell'*/
{
result = PyObject_CallFunction(helper->seek, "ii", offset, whence);
if(!result) {
PyErr_Clear();
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
Py_DECREF(result);
}
result = PyObject_CallFunction(helper->tell, NULL);
if(!result) {
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
retval = PyInt_AsLong(result);
Py_DECREF(result);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return retval;
}
static int rw_read_th(SDL_RWops* context, void* ptr, int size, int maxnum)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval;
PyThreadState* oldstate;
if(!helper->read)
return -1;
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(helper->thread);
result = PyObject_CallFunction(helper->read, "i", size * maxnum);
if(!result) {
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
if(!PyString_Check(result))
{
Py_DECREF(result);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
retval = PyString_GET_SIZE(result);
memcpy(ptr, PyString_AsString(result), retval);
retval /= size;
Py_DECREF(result);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return retval;
}
static int rw_write_th(SDL_RWops* context, const void* ptr, int size, int num)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
PyThreadState* oldstate;
if(!helper->write)
return -1;
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(helper->thread);
result = PyObject_CallFunction(helper->write, "s#", ptr, size * num);
if(!result) {
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
Py_DECREF(result);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return num;
}
static int rw_close_th(SDL_RWops* context)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval = 0;
PyThreadState* oldstate;
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(helper->thread);
if(helper->close)
{
result = PyObject_CallFunction(helper->close, NULL);
if(result)
retval = -1;
Py_XDECREF(result);
}
PyThreadState_Swap(oldstate);
PyThreadState_Clear(helper->thread);
PyThreadState_Delete(helper->thread);
Py_XDECREF(helper->seek);
Py_XDECREF(helper->tell);
Py_XDECREF(helper->write);
Py_XDECREF(helper->read);
Py_XDECREF(helper->close);
PyMem_Del(helper);
PyEval_ReleaseLock();
SDL_FreeRW(context);
return retval;
}
#endif
+18 -16
View File
@@ -37,6 +37,7 @@ library("png")
library("avformat")
library("avcodec")
library("avutil")
has_avresample = library("avresample", optional=True)
has_swscale = library("swscale", optional=True)
library("freetype")
has_fribidi = library("fribidi", optional=True)
@@ -46,7 +47,7 @@ has_libglew32 = library("glew32", optional=True)
has_angle = windows and library("EGL", optional=True) and library("GLESv2", optional=True)
if android:
sdl = [ 'sdl', 'GLESv1_CM', 'log' ]
sdl = [ 'sdl', 'GLESv2', 'log' ]
else:
sdl = [ 'SDL' ]
@@ -54,15 +55,9 @@ else:
# Modules directory.
cython(
"_renpy",
[ "IMG_savepng.c", "core.c", "rwobject.c", "subpixel.c"],
[ "IMG_savepng.c", "core.c", "subpixel.c"],
sdl + [ 'png', 'z', 'm' ])
cmodule(
"_renpy_font",
[ "renpy_ttf.c", "renpy_font.c"],
sdl + [ 'freetype', 'z', 'm' ],
)
if has_fribidi and not android:
cython(
"_renpybidi",
@@ -75,22 +70,29 @@ pymodule("pysdlsound.__init__")
if not android:
sound = [ "avformat", "avcodec", "avutil", "z" ]
macros = [ ]
if has_avresample:
sound.insert(0, "avresample")
macros.append(("HAS_RESAMPLE", 1))
if has_swscale:
sound.insert(0, "swscale")
cython(
"pysdlsound.sound",
[ "pss.c", "rwobject.c", "ffdecode.c" ],
libs = sdl + sound)
[ "pss.c", "ffdecode.c" ],
libs = sdl + sound,
define_macros=macros)
# Display.
cython("renpy.display.render", libs=[ 'z', 'm' ])
cython("renpy.display.accelerator", libs=[ 'z', 'm' ])
cython("renpy.display.accelerator", libs=sdl + [ 'z', 'm' ])
# Gl.
if android:
glew_libs = [ 'GLESv1_CM', 'z', 'm' ]
glew_libs = [ 'GLESv2', 'z', 'm' ]
elif has_libglew:
glew_libs = [ 'GLEW' ]
else:
@@ -98,9 +100,9 @@ else:
cython("renpy.gl.gldraw", libs=glew_libs )
cython("renpy.gl.gltexture", libs=glew_libs)
cython("renpy.gl.glenviron_shader", libs=glew_libs)
cython("renpy.gl.glenviron_fixed", libs=glew_libs, compile_if=not android)
cython("renpy.gl.glenviron_shader", libs=glew_libs, compile_if=not android)
cython("renpy.gl.glenviron_limited", libs=glew_libs)
cython("renpy.gl.glenviron_limited", libs=glew_libs, compile_if=not android)
cython("renpy.gl.glrtt_copy", libs=glew_libs)
cython("renpy.gl.glrtt_fbo", libs=glew_libs)
@@ -138,7 +140,7 @@ cython("renpy.text.texwrap")
cython(
"renpy.text.ftfont",
[ "ftsupport.c" ],
[ "ftsupport.c", "ttgsubtable.c" ],
libs = sdl + [ 'freetype', 'z', 'm' ])
find_unnecessary_gen()
+513
View File
@@ -0,0 +1,513 @@
/*
Copyright 2012 Koichi Akabe <vbkaisetsu@gmail.com>
The original C++ source is distributed under the PD-like license.
For more details, see:
http://higambana.ashigaru.jp/
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include "ttgsubtable.h"
uint8_t GetUInt8(FT_Bytes *p)
{
uint8_t ret = (*p)[0];
*p += 1;
return ret;
}
int16_t GetInt16(FT_Bytes *p)
{
uint16_t ret = (*p)[0] << 8 | (*p)[1];
*p += 2;
return *(int16_t*)&ret;
}
uint16_t GetUInt16(FT_Bytes *p)
{
uint16_t ret = (*p)[0] << 8 | (*p)[1];
*p += 2;
return ret;
}
int32_t GetInt32(FT_Bytes *p)
{
uint32_t ret = (*p)[0] << 24 | (*p)[1] << 16 | (*p)[2] << 8 | (*p)[3];
*p += 4;
return *(int32_t*)&ret;
}
uint32_t GetUInt32(FT_Bytes *p)
{
uint32_t ret = (*p)[0] << 24 | (*p)[1] << 16 | (*p)[2] << 8 | (*p)[3];
*p += 4;
return ret;
}
void init_gsubtable(TTGSUBTable *table)
{
table->loaded = 0;
table->ScriptList.ScriptCount = 0;
table->ScriptList.ScriptRecord = NULL;
table->FeatureList.FeatureCount = 0;
table->FeatureList.FeatureRecord = NULL;
table->LookupList.LookupCount = 0;
table->LookupList.Lookup = NULL;
}
void free_gsubtable(TTGSUBTable *table)
{
if(table->loaded == 0)
{
return;
}
int i, j;
int scr_cnt = table->ScriptList.ScriptCount;
TScriptRecord *scr_rcd = table->ScriptList.ScriptRecord;
for(i = 0; i < scr_cnt; i++)
{
int ls_cnt = scr_rcd[i].Script.LangSysCount;
TLangSysRecord *ls_rcd = scr_rcd[i].Script.LangSysRecord;
for(j = 0; j < ls_cnt; j++)
{
free(ls_rcd[j].LangSys.FeatureIndex);
}
free(ls_rcd);
}
free(scr_rcd);
int ftr_cnt = table->FeatureList.FeatureCount;
TFeatureRecord *ftr_rcd = table->FeatureList.FeatureRecord;
for(i = 0; i < ftr_cnt; i++)
{
free(ftr_rcd[i].Feature.LookupListIndex);
}
free(ftr_rcd);
int lup_cnt = table->LookupList.LookupCount;
TLookup *lup = table->LookupList.Lookup;
for(i = 0; i < lup_cnt; i++)
{
int ls_cnt = lup[i].SubTableCount;
TSingleSubstFormat *subt = lup[i].SubTable;
for(j = 0; j < ls_cnt; j++)
{
if(subt[j].Coverage.CoverageFormat == 1) {
free(subt[j].Coverage.GlyphArray);
} else if(subt[j].Coverage.CoverageFormat == 2) {
free(subt[j].Coverage.RangeRecord);
}
if(subt[j].SubstFormat == 2)
free(subt[j].Substitute);
}
free(subt);
}
free(lup);
}
int LoadGSUBTable2(TTGSUBTable *table, FT_Bytes gsub)
{
table->header.Version = gsub[0] << 24 | gsub[1] << 16 | gsub[2] << 8 | gsub[3];
if(table->header.Version != 0x00010000)
{
return -1;
}
table->header.ScriptList = gsub[4] << 8 | gsub[5];
table->header.FeatureList = gsub[6] << 8 | gsub[7];
table->header.LookupList = gsub[8] << 8 | gsub[9];
return Parse(table, &gsub[table->header.ScriptList], &gsub[table->header.FeatureList], &gsub[table->header.LookupList]);
}
int GetVerticalGlyph(TTGSUBTable *table, uint32_t glyphnum, uint32_t *vglyphnum)
{
int i, j;
uint32_t tag[] = {
(uint8_t)'v' << 24 |
(uint8_t)'r' << 16 |
(uint8_t)'t' << 8 |
(uint8_t)'2',
(uint8_t)'v' << 24 |
(uint8_t)'e' << 16 |
(uint8_t)'r' << 8 |
(uint8_t)'t',
};
if(!table->loaded)
{
return -1;
}
for(i = 0; i < 2; i++)
{
for(j = 0; j < table->FeatureList.FeatureCount; j++)
{
if(table->FeatureList.FeatureRecord[j].FeatureTag == tag[i])
{
if(GetVerticalGlyphSub(table, glyphnum, vglyphnum, &table->FeatureList.FeatureRecord[j].Feature) == 0)
{
return 0;
}
}
}
}
return -1;
}
int GetVerticalGlyphSub(TTGSUBTable *table, uint32_t glyphnum, uint32_t *vglyphnum, TFeature *Feature)
{
int i, index;
for(i = 0; i < Feature->LookupCount; i++)
{
index = Feature->LookupListIndex[i];
if(index < 0 || table->LookupList.LookupCount < index)
{
continue;
}
if(table->LookupList.Lookup[index].LookupType == 1)
{
if(GetVerticalGlyphSub2(table, glyphnum, vglyphnum, &table->LookupList.Lookup[index]) == 0)
{
return 0;
}
}
}
return -1;
}
int GetVerticalGlyphSub2(TTGSUBTable *table, uint32_t glyphnum, uint32_t *vglyphnum, TLookup *Lookup)
{
int i, index;
TSingleSubstFormat *tbl;
for(i = 0; i < Lookup->SubTableCount; i++)
{
switch(Lookup->SubTable[i].SubstFormat)
{
case 1:
tbl = &Lookup->SubTable[i];
if(GetCoverageIndex(table, &tbl->Coverage, glyphnum) >= 0)
{
*vglyphnum = glyphnum + tbl->DeltaGlyphID;
return 0;
}
break;
case 2:
tbl = &Lookup->SubTable[i];
index = GetCoverageIndex(table, &tbl->Coverage, glyphnum);
if(0 <= index && index < tbl->GlyphCount)
{
*vglyphnum = tbl->Substitute[index];
return 0;
}
break;
}
}
return -1;
}
int GetCoverageIndex(TTGSUBTable *table, TCoverageFormat *Coverage, uint32_t g)
{
int i;
switch(Coverage->CoverageFormat)
{
case 1:
for(i = 0; i < Coverage->GlyphCount; i++)
{
if((uint32_t)Coverage->GlyphArray[i] == g)
{
return i;
}
}
return -1;
case 2:
for(i = 0; i < Coverage->RangeCount; i++)
{
uint32_t s = Coverage->RangeRecord[i].Start;
uint32_t e = Coverage->RangeRecord[i].End;
uint32_t si = Coverage->RangeRecord[i].StartCoverageIndex;
if(si + s <= g && g <= si + e)
{
return si + g - s;
}
}
return -1;
}
return -1;
}
int Parse(TTGSUBTable *table, FT_Bytes scriptlist, FT_Bytes featurelist, FT_Bytes lookuplist)
{
ParseScriptList(table, scriptlist, &table->ScriptList);
ParseFeatureList(table, featurelist, &table->FeatureList);
ParseLookupList(table, lookuplist, &table->LookupList);
return 0;
}
void ParseScriptList(TTGSUBTable *table, FT_Bytes raw, TScriptList *rec)
{
int i;
FT_Bytes sp = raw;
rec->ScriptCount = GetUInt16(&sp);
if(rec->ScriptCount <= 0)
{
rec->ScriptRecord = NULL;
return;
}
rec->ScriptRecord = calloc(rec->ScriptCount, sizeof(TScriptRecord));
for(i = 0; i < rec->ScriptCount; i++)
{
rec->ScriptRecord[i].ScriptTag = GetUInt32(&sp);
uint16_t offset = GetUInt16(&sp);
ParseScript(table, &raw[offset], &rec->ScriptRecord[i].Script);
}
}
void ParseScript(TTGSUBTable *table, FT_Bytes raw, TScript *rec)
{
int i;
FT_Bytes sp = raw;
rec->DefaultLangSys = GetUInt16(&sp);
rec->LangSysCount = GetUInt16(&sp);
if(rec->LangSysCount <= 0)
{
rec->LangSysRecord = NULL;
return;
}
rec->LangSysRecord = calloc(rec->LangSysCount, sizeof(TLangSysRecord));
for(i = 0; i < rec->LangSysCount; i++)
{
rec->LangSysRecord[i].LangSysTag = GetUInt32(&sp);
uint16_t offset = GetUInt16(&sp);
ParseLangSys(table, &raw[offset], &rec->LangSysRecord[i].LangSys);
}
}
void ParseLangSys(TTGSUBTable *table, FT_Bytes raw, TLangSys *rec)
{
FT_Bytes sp = raw;
rec->LookupOrder = GetUInt16(&sp);
rec->ReqFeatureIndex = GetUInt16(&sp);
rec->FeatureCount = GetUInt16(&sp);
if(rec->FeatureCount <= 0)
return;
rec->FeatureIndex = calloc(rec->FeatureCount, sizeof(uint16_t));
}
void ParseFeatureList(TTGSUBTable *table, FT_Bytes raw, TFeatureList *rec)
{
int i;
FT_Bytes sp = raw;
rec->FeatureCount = GetUInt16(&sp);
if(rec->FeatureCount <= 0)
{
rec->FeatureRecord = NULL;
return;
}
rec->FeatureRecord = calloc(rec->FeatureCount, sizeof(TFeatureRecord));
for(i = 0; i < rec->FeatureCount; i++)
{
rec->FeatureRecord[i].FeatureTag = GetUInt32(&sp);
uint16_t offset = GetUInt16(&sp);
ParseFeature(table, &raw[offset], &rec->FeatureRecord[i].Feature);
}
}
void ParseFeature(TTGSUBTable *table, FT_Bytes raw, TFeature *rec)
{
int i;
FT_Bytes sp = raw;
rec->FeatureParams = GetUInt16(&sp);
rec->LookupCount = GetUInt16(&sp);
if(rec->LookupCount <= 0)
{
return;
}
rec->LookupListIndex = calloc(rec->LookupCount, sizeof(uint16_t));
for(i = 0;i < rec->LookupCount; i++)
{
rec->LookupListIndex[i] = GetUInt16(&sp);
}
}
void ParseLookupList(TTGSUBTable *table, FT_Bytes raw, TLookupList *rec)
{
int i;
FT_Bytes sp = raw;
rec->LookupCount = GetUInt16(&sp);
if(rec->LookupCount <= 0)
{
rec->Lookup = NULL;
return;
}
rec->Lookup = calloc(rec->LookupCount, sizeof(TLookup));
for(i = 0; i < rec->LookupCount; i++)
{
uint16_t offset = GetUInt16(&sp);
ParseLookup(table, &raw[offset], &rec->Lookup[i]);
}
}
void ParseLookup(TTGSUBTable *table, FT_Bytes raw, TLookup *rec)
{
int i;
FT_Bytes sp = raw;
rec->LookupType = GetUInt16(&sp);
rec->LookupFlag = GetUInt16(&sp);
rec->SubTableCount = GetUInt16(&sp);
if(rec->SubTableCount <= 0)
{
rec->SubTable = NULL;
return;
}
rec->SubTable = calloc(rec->SubTableCount, sizeof(TSingleSubstFormat));
if(rec->LookupType != 1)
return;
for(i = 0; i < rec->SubTableCount; i++)
{
uint16_t offset = GetUInt16(&sp);
ParseSingleSubst(table, &raw[offset], &rec->SubTable[i]);
}
}
void ParseCoverage(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec)
{
FT_Bytes sp = raw;
uint16_t Format = GetUInt16(&sp);
switch(Format)
{
case 1:
rec->CoverageFormat = 1;
ParseCoverageFormat1(table, raw, rec);
break;
case 2:
rec->CoverageFormat = 2;
ParseCoverageFormat2(table, raw, rec);
break;
default:
rec->CoverageFormat = 0;
}
}
void ParseCoverageFormat1(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec)
{
int i;
FT_Bytes sp = raw;
GetUInt16(&sp);
rec->GlyphCount = GetUInt16(&sp);
if(rec->GlyphCount <= 0)
{
rec->GlyphArray = NULL;
return;
}
rec->GlyphArray = calloc(rec->GlyphCount, sizeof(uint16_t));
for(i = 0; i < rec->GlyphCount; i++)
{
rec->GlyphArray[i] = GetUInt16(&sp);
}
}
void ParseCoverageFormat2(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec)
{
int i;
FT_Bytes sp = raw;
GetUInt16(&sp);
rec->RangeCount = GetUInt16(&sp);
if(rec->RangeCount <= 0)
{
rec->RangeRecord = NULL;
return;
}
rec->RangeRecord = calloc(rec->RangeCount, sizeof(TRangeRecord));
for(i = 0; i < rec->RangeCount; i++)
{
rec->RangeRecord[i].Start = GetUInt16(&sp);
rec->RangeRecord[i].End = GetUInt16(&sp);
rec->RangeRecord[i].StartCoverageIndex = GetUInt16(&sp);
}
}
void ParseSingleSubst(TTGSUBTable *table, FT_Bytes raw, TSingleSubstFormat *rec)
{
FT_Bytes sp = raw;
uint16_t Format = GetUInt16(&sp);
switch(Format)
{
case 1:
rec->SubstFormat = 1;
ParseSingleSubstFormat1(table, raw, rec);
break;
case 2:
rec->SubstFormat = 2;
ParseSingleSubstFormat2(table, raw, rec);
break;
default:
rec->SubstFormat = 0;
}
}
void ParseSingleSubstFormat1(TTGSUBTable *table, FT_Bytes raw, TSingleSubstFormat *rec)
{
FT_Bytes sp = raw;
GetUInt16(&sp);
uint16_t offset = GetUInt16(&sp);
ParseCoverage(table, &raw[offset], &rec->Coverage);
rec->DeltaGlyphID = GetInt16(&sp);
}
void ParseSingleSubstFormat2(TTGSUBTable *table, FT_Bytes raw, TSingleSubstFormat *rec)
{
int i;
FT_Bytes sp = raw;
GetUInt16(&sp);
uint16_t offset = GetUInt16(&sp);
ParseCoverage(table, &raw[offset], &rec->Coverage);
rec->GlyphCount = GetUInt16(&sp);
if(rec->GlyphCount <= 0)
{
rec->Substitute = NULL;
return;
}
rec->Substitute = calloc(rec->GlyphCount, sizeof(uint16_t));
for(i = 0; i < rec->GlyphCount; i++)
{
rec->Substitute[i] = GetUInt16(&sp);
}
}
void LoadGSUBTable(TTGSUBTable *table, FT_Face face)
{
FT_Bytes base = NULL;
FT_Bytes gdef = NULL;
FT_Bytes gpos = NULL;
FT_Bytes gsub = NULL;
FT_Bytes jstf = NULL;
FT_OpenType_Validate(face, FT_VALIDATE_GSUB, &base, &gdef, &gpos, &gsub, &jstf);
if(gsub == NULL)
{
table->loaded = 0;
return;
}
int ret = LoadGSUBTable2(table, gsub);
FT_OpenType_Free(face, gsub);
if(ret != 0)
{
table->loaded = 0;
return;
}
table->loaded = 1;
}
+146
View File
@@ -0,0 +1,146 @@
#ifndef TTGSUBTable_H
#define TTGSUBTable_H
#include <stdint.h>
#include <ft2build.h>
#include <freetype/ftotval.h>
typedef struct
{
uint32_t Version;
uint16_t ScriptList;
uint16_t FeatureList;
uint16_t LookupList;
} tt_gsub_header;
typedef struct
{
uint16_t LookupOrder;
uint16_t ReqFeatureIndex;
uint16_t FeatureCount;
uint16_t *FeatureIndex;
} TLangSys;
typedef struct
{
uint32_t LangSysTag;
TLangSys LangSys;
} TLangSysRecord;
typedef struct
{
uint16_t DefaultLangSys;
uint16_t LangSysCount;
TLangSysRecord *LangSysRecord;
} TScript;
typedef struct
{
uint32_t ScriptTag;
TScript Script;
} TScriptRecord;
typedef struct
{
uint16_t ScriptCount;
TScriptRecord *ScriptRecord;
} TScriptList;
typedef struct
{
uint16_t FeatureParams;
int LookupCount;
uint16_t *LookupListIndex;
} TFeature;
typedef struct
{
uint32_t FeatureTag;
TFeature Feature;
} TFeatureRecord;
typedef struct
{
int FeatureCount;
TFeatureRecord *FeatureRecord;
} TFeatureList;
typedef struct
{
uint16_t Start;
uint16_t End;
uint16_t StartCoverageIndex;
} TRangeRecord;
typedef struct
{
uint16_t CoverageFormat;
uint16_t GlyphCount;
uint16_t *GlyphArray;
uint16_t RangeCount;
TRangeRecord *RangeRecord;
} TCoverageFormat;
typedef struct
{
uint16_t SubstFormat;
TCoverageFormat Coverage;
int16_t DeltaGlyphID;
uint16_t GlyphCount;
uint16_t *Substitute;
} TSingleSubstFormat;
typedef struct
{
uint16_t LookupType;
uint16_t LookupFlag;
uint16_t SubTableCount;
TSingleSubstFormat *SubTable;
} TLookup;
typedef struct
{
int LookupCount;
TLookup *Lookup;
} TLookupList;
typedef struct
{
int loaded;
tt_gsub_header header;
TScriptList ScriptList;
TFeatureList FeatureList;
TLookupList LookupList;
} TTGSUBTable;
void LoadGSUBTable(TTGSUBTable *table, FT_Face face);
int LoadGSUBTable2(TTGSUBTable *table, FT_Bytes gsub);
int GetVerticalGlyph(TTGSUBTable *table, uint32_t glyphnum, uint32_t *vglyphnum);
int Parse(TTGSUBTable *table, FT_Bytes scriptlist, FT_Bytes featurelist, FT_Bytes lookuplist);
void ParseScriptList(TTGSUBTable *table, FT_Bytes raw, TScriptList *rec);
void ParseScript(TTGSUBTable *table, FT_Bytes raw, TScript *rec);
void ParseLangSys(TTGSUBTable *table, FT_Bytes raw, TLangSys *rec);
void ParseFeatureList(TTGSUBTable *table, FT_Bytes raw, TFeatureList *rec);
void ParseFeature(TTGSUBTable *table, FT_Bytes raw, TFeature *rec);
void ParseLookupList(TTGSUBTable *table, FT_Bytes raw, TLookupList *rec);
void ParseLookup(TTGSUBTable *table, FT_Bytes raw, TLookup *rec);
void ParseCoverage(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec);
void ParseCoverageFormat1(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec);
void ParseCoverageFormat2(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec);
void ParseSingleSubst(TTGSUBTable *table, FT_Bytes raw, TSingleSubstFormat *rec);
void ParseSingleSubstFormat1(TTGSUBTable *table, FT_Bytes raw, TSingleSubstFormat *rec);
void ParseSingleSubstFormat2(TTGSUBTable *table, FT_Bytes raw, TSingleSubstFormat *rec);
int GetVerticalGlyphSub(TTGSUBTable *table, uint32_t glyphnum, uint32_t *vglyphnum, TFeature *Feature);
int GetVerticalGlyphSub2(TTGSUBTable *table, uint32_t glyphnum, uint32_t *vglyphnum, TLookup *Lookup);
int GetCoverageIndex(TTGSUBTable *table, TCoverageFormat *Coverage, uint32_t g);
void init_gsubtable(TTGSUBTable *table);
void free_gsubtable(TTGSUBTable *table);
#endif
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

-1
View File
@@ -1 +0,0 @@
zip -r renpygame-$1.zip renpygame-demo -x \*.svn\* -x \*saves\* -x \*.pyc -x \*.pyo -x \*~
+27 -7
View File
@@ -1,5 +1,9 @@
#!/usr/bin/env python
#@PydevCodeAnalysisIgnore
# This file is part of Ren'Py. The license below applies to Ren'Py only.
# Games and other projects that use Ren'Py may use a different license.
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
@@ -30,13 +34,33 @@ import warnings
# Given the Ren'Py base directory (usually the directory containing
# this file), this is expected to return the path to the common directory.
def path_to_common(renpy_base):
return renpy_base + "/common"
return renpy_base + "/renpy/common"
# Given a directory holding a Ren'Py game, this is expected to return
# the path to a directory that will hold save files.
def path_to_saves(gamedir):
import renpy #@UnresolvedImport
# Android.
if renpy.android:
paths = [
os.path.join(os.environ["ANDROID_OLD_PUBLIC"], "game/saves"),
os.path.join(os.environ["ANDROID_PRIVATE"], "saves"),
os.path.join(os.environ["ANDROID_PUBLIC"], "saves"),
]
for rv in paths:
if os.path.isdir(rv):
break
print "Using savedir", rv
# We return the last path as the default.
return rv
# No save directory given.
if not renpy.config.save_directory:
return gamedir + "/saves"
@@ -54,10 +78,7 @@ def path_to_saves(gamedir):
path = newpath
# Otherwise, put the saves in a platform-specific location.
if renpy.android:
return gamedir + "/saves"
elif renpy.macintosh:
if renpy.macintosh:
rv = "~/Library/RenPy/" + renpy.config.save_directory
return os.path.expanduser(rv)
@@ -76,7 +97,7 @@ def path_to_saves(gamedir):
# Returns the path to the Ren'Py base directory (containing common and
# the launcher, usually.)
def path_to_renpy_base():
renpy_base = os.path.dirname(sys.argv[0])
renpy_base = os.path.dirname(os.path.realpath(sys.argv[0]))
renpy_base = os.environ.get('RENPY_BASE', renpy_base)
renpy_base = os.path.abspath(renpy_base)
@@ -107,7 +128,6 @@ if android:
__main__.path_to_common = path_to_common
__main__.path_to_saves = path_to_saves
os.environ["RENPY_RENDERER"] = "gl"
os.environ["RENPY_GL_ENVIRON"] = "limited"
def main():
+21 -39
View File
@@ -7,56 +7,38 @@ ROOT=$(cd "$ROOT"; pwd)
# The name of this shell script without the .sh on the end.
BASEFILE=$(basename "$0" .sh)
# The full path to the shell script, without the .sh at the end of it.
BASE="$ROOT/$BASEFILE"
# Assume Darwin means Mac OS X, and launch us using the OS X launcher.
if [ "$(uname -s)" = "Darwin" ] ; then
RENPY_LAUNCHER_DIR="$ROOT"
export RENPY_LAUNCHER_DIR
if [ -e "${BASE}.app/Contents/MacOS/${BASEFILE}" ] ; then
LAUNCHER="${BASE}.app/Contents/MacOS/${BASEFILE}"
else
LAUNCHER="${BASE}.app/Contents/MacOS/Ren'Py Launcher"
fi
exec $RENPY_GDB "${LAUNCHER}" "${BASE}.py" "$@"
fi
# Otherwise, assume we're on linux, or an OS that can run Linux binaries.
# If that's not the case, you'll have to change this script.
if [ -z "$RENPY_PLATFORM" ] ; then
case `uname -m` in
x86_64|amd64)
case "$(uname -s)-$(uname -m)" in
Darwin-*)
RENPY_PLATFORM="darwin-x86_64"
ROOT1="$ROOT/../Resources/autorun"
ROOT2="$ROOT/../../.."
;;
*-x86_64|amd64)
RENPY_PLATFORM="linux-x86_64"
;;
i*86)
ROOT1="$ROOT"
ROOT2="$ROOT"
;;
*-i*86)
RENPY_PLATFORM="linux-i686"
ROOT1="$ROOT"
ROOT2="$ROOT"
;;
*)
echo "Ren'Py could not detect that platform it's running on. Please set"
echo "the RENPY_PLATFORM environment variable to one of \"linux-i686\" or"
echo "\"linux-x86_64\", and run this command again."
echo "\"linux-x86_64\", or \"darwin-x86_64\" and run this command again."
exit 1
;;
esac
fi
LIB="$ROOT/lib/$RENPY_PLATFORM"
RENPY_ORIGINAL_LD_LIBRARY_PATH="$LD_LIBRARY_PATH"
for BASE in "$ROOT" "$ROOT1" "$ROOT2"; do
LIB="$BASE/lib/$RENPY_PLATFORM"
if test -d "$LIB"; then
break
fi
done
if [ -z "$LD_LIBRARY_PATH" ] ; then
LD_LIBRARY_PATH="$LIB"
else
LD_LIBRARY_PATH="$LIB:$LD_LIBRARY_PATH"
fi
export LD_LIBRARY_PATH
export RENPY_PLATFORM
export RENPY_ORIGINAL_LD_LIBRARY_PATH
exec $RENPY_GDB "$LIB/python" $RENPY_PYARGS -OO "$BASE.py" "$@"
exec $RENPY_GDB "$LIB/$BASEFILE" $RENPY_PYARGS -EOO "$BASE/$BASEFILE.py" "$@"
+6 -6
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
@@ -34,9 +34,9 @@ except ImportError:
# The tuple giving the version. This needs to be updated when
# we bump the version.
#
# Be sure to change script_version in launcher/script_version.rpy.
# Be sure to change script_version in launcher/game/script_version.rpy.
# Be sure to change config.version in tutorial/game/options.rpy.
version_tuple = (6, 14, 1, vc_version)
version_tuple = (6, 15, 7, vc_version)
# A verbose string computed from that version.
version = "Ren'Py " + ".".join(str(i) for i in version_tuple)
@@ -114,6 +114,7 @@ def import_all():
import renpy.statements #@UnresolvedImport
import renpy.style #@UnresolvedImport
import renpy.substitutions #@UnresolvedImport
import renpy.translation #@UnresolvedImport
import renpy.display.presplash #@UnresolvedImport
import renpy.display.pgrender #@UnresolvedImport
@@ -235,9 +236,6 @@ def reload_all():
# Shut down the cache thread.
renpy.display.im.cache.quit()
# Cleans out the RenpyImporter.
sys.meta_path.pop()
blacklist = [ "renpy",
"renpy.log",
"renpy.bootstrap",
@@ -276,3 +274,5 @@ else:
linux = True
# The android init code in renpy.py will set linux=False and android=True.

Some files were not shown because too many files have changed in this diff Show More