smc-get 0.1.0 → 0.2.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+
4
+ module SmcGet
5
+
6
+ class Repository
7
+
8
+ end
9
+
10
+ end
@@ -1,139 +1,117 @@
1
- #Encoding: UTF-8
2
- ################################################################################
3
- # This file is part of smc-get.
4
- # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
- # Copyright (C) 2011 Marvin Gülker
6
- #
7
- # This program is free software: you can redistribute it and/or modify
8
- # it under the terms of the GNU General Public License as published by
9
- # the Free Software Foundation, either version 3 of the License, or
10
- # (at your option) any later version.
11
- #
12
- # This program is distributed in the hope that it will be useful,
13
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- # GNU General Public License for more details.
16
- #
17
- # You should have received a copy of the GNU General Public License
18
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
- ################################################################################
20
-
21
- require "pathname"
22
- require 'tempfile'
23
- require "fileutils"
24
- begin
25
- require "psych"
26
- YAML = Psych
27
- rescue LoadError
28
- require 'yaml'
29
- end
30
- require 'uri'
31
- require 'net/https'
32
-
33
- require_relative "./errors"
34
- require_relative "./package"
35
-
36
- #This is the main module of smc-get and it's namespace.
37
- module SmcGet
38
-
39
- #Root directory of the smc-get program and libraries.
40
- ROOT_DIR = Pathname.new(__FILE__).dirname.parent.parent
41
- #Subdirectory for executable files.
42
- BIN_DIR = ROOT_DIR + "bin"
43
- #Subdirectory for library files.
44
- LIB_DIR = ROOT_DIR + "lib"
45
- #Subdirectory for configuration files.
46
- CONFIG_DIR = ROOT_DIR + "config"
47
-
48
- #Directory where the package specifications are saved to. Relative to the
49
- #data directory in SmcGet.datadir.
50
- PACKAGE_SPECS_DIR = "packages".freeze
51
- #Directory where a package's music files are saved to. Relative to the
52
- #data directory in SmcGet.datadir.
53
- PACKAGE_MUSIC_DIR = "music/contrib-music".freeze
54
- #Directory where a package's graphics files are saved to. Relative to the
55
- #data directory given in SmcGet.datadir
56
- PACKAGE_GRAPHICS_DIR = "pixmaps/contrib-graphics".freeze
57
- #Directory where a package's level files are saved to. Relative to the
58
- #data directory given in SmcGet.datadir.
59
- PACKAGE_LEVELS_DIR = "levels".freeze
60
- #The name of the file containing the list of all levels in the
61
- #repository.
62
- PACKAGE_LIST_FILE = "#{PACKAGE_SPECS_DIR}/packages.lst".freeze
63
- #The version of smc-get.
64
- VERSION = Pathname.new(__FILE__).dirname.expand_path.join("..", "..", "VERSION.txt").read.chomp
65
-
66
- class << self
67
-
68
- #The URL of the repository.
69
- attr_reader :repo_url
70
- #The directory where SMC is installed.
71
- attr_reader :datadir
72
-
73
- @repo_url = nil
74
- @datadir = nil
75
-
76
- #Initializes the library. Pass in the URL from which you want
77
- #to downloaded packages (most likely Luiji's contributed level
78
- #repository at <tt>https://github.com/Luiji/Secret-Maryo-Chronicles-Contributed-Levels/raw/master/</tt>)
79
- #and the directory where SMC is installed (something along the lines of
80
- #<b>/usr/share/smc</b>). Note you *have* to call this method before
81
- #you can make use of the smc-get library; otherwise you'll get bombed
82
- #by SmcGet::Errors::LibraryNotInitialized exceptions.
83
- #
84
- #You may call this method more than once if you want to reinitialize
85
- #the library to use other resources.
86
- def setup(repo_url, datadir)
87
- @repo_url = repo_url
88
- @datadir = Pathname.new(datadir)
89
- end
90
-
91
- # Download the specified raw file from the repository to the specified
92
- # output file. URL should be everything in the URL after
93
- # SmcGet.repo_url.
94
- # Yields the currently downloaded file and how many percent of that file have
95
- # already been downloaded if a block is given.
96
- def download(url, output) # :nodoc:
97
- Errors::LibraryNotInitialized.throw_if_needed!
98
- # Make url friendly.
99
- url = URI::Parser.new.escape(url)
100
- # Create directories if needed.
101
- FileUtils.mkdir_p(File.dirname(output))
102
- # Download file.
103
- File.open(output, "w") do |outputfile|
104
- uri = URI.parse(@repo_url + url)
105
-
106
- request = Net::HTTP.new(uri.host, uri.port)
107
- request.use_ssl = true #GitHub uses SSL
108
-
109
- begin
110
- request.start do
111
- #1. Establish connection
112
- request.request_get(uri.path) do |response|
113
- raise(Errors::DownloadFailedError.new(url), "ERROR: Received HTTP error code #{response.code}.") unless response.code == "200"
114
- #2. Get what size the file is
115
- final_size = response.content_length
116
- current_size = 0
117
- #Ensure the first value the user sees are 0%
118
- yield(url, 0) if block_given?
119
- #3. Get the actual file in parts and report percent done.
120
- response.read_body do |part|
121
- outputfile.write(part)
122
-
123
- current_size += part.size
124
- percent = (current_size.to_f / final_size.to_f) * 100
125
- yield(url, percent) if block_given?
126
- end
127
- end
128
- #Ensure the last value the user sees are 100%
129
- yield(url, 100) if block_given?
130
- end
131
- rescue Timeout::Error
132
- raise(Errors::ConnectionTimedOutError.new(url), "ERROR: Connection timed out.")
133
- end
134
- end
135
- end
136
-
137
- end
138
-
139
- end
1
+ #Encoding: UTF-8
2
+ ################################################################################
3
+ # This file is part of smc-get.
4
+ # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
+ # Copyright (C) 2011 Marvin Gülker
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ ################################################################################
20
+
21
+ require "pathname"
22
+ require 'tempfile'
23
+ require "fileutils"
24
+ require "tempfile"
25
+ require "digest/sha1"
26
+ begin
27
+ require "psych"
28
+ rescue LoadError
29
+ end
30
+ require "yaml"
31
+ require 'uri'
32
+ require "open-uri"
33
+ require 'net/https'
34
+ require "archive/tar/minitar"
35
+ require "xz"
36
+
37
+ require_relative "./errors"
38
+ require_relative "./repository"
39
+ require_relative "./local_repository"
40
+ require_relative "./remote_repository"
41
+ require_relative "./package_archive"
42
+ require_relative "./package_specification"
43
+ require_relative "./package"
44
+
45
+ #Extend the Hash class with a method to turn all keys into symbols.
46
+ class Hash
47
+
48
+ #Recursively turns all keys in this hash into symbols. This method
49
+ #is inteded for loading configuration files. Doesn’t modify the receiver.
50
+ def symbolic_keys
51
+ inject({}){|hsh, (k, v)| hsh[k.to_sym] = v.respond_to?(:symbolic_keys) ? v.symbolic_keys : v; hsh}
52
+ end
53
+
54
+ end
55
+
56
+ #This is the main module of smc-get and it's namespace.
57
+ module SmcGet
58
+
59
+ #Root directory of the smc-get program and libraries.
60
+ ROOT_DIR = Pathname.new(__FILE__).dirname.parent.parent
61
+ #Subdirectory for executable files.
62
+ BIN_DIR = ROOT_DIR + "bin"
63
+ #Subdirectory for library files.
64
+ LIB_DIR = ROOT_DIR + "lib"
65
+ #Subdirectory for configuration files.
66
+ CONFIG_DIR = ROOT_DIR + "config"
67
+
68
+ #Directory where the package specifications are saved to. Relative to the
69
+ #data directory in SmcGet.datadir.
70
+ PACKAGE_SPECS_DIR = "packages".freeze
71
+ #Directory where a package's music files are saved to. Relative to the
72
+ #data directory in SmcGet.datadir.
73
+ PACKAGE_MUSIC_DIR = "music/contrib-music".freeze
74
+ #Directory where a package's graphics files are saved to. Relative to the
75
+ #data directory given in SmcGet.datadir
76
+ PACKAGE_GRAPHICS_DIR = "pixmaps/contrib-graphics".freeze
77
+ #Directory where a package's level files are saved to. Relative to the
78
+ #data directory given in SmcGet.datadir.
79
+ PACKAGE_LEVELS_DIR = "levels".freeze
80
+ #The name of the file containing the list of all levels in the
81
+ #repository.
82
+ PACKAGE_LIST_FILE = "#{PACKAGE_SPECS_DIR}/packages.lst".freeze
83
+ #The version of smc-get.
84
+ Pathname.new(__FILE__).dirname.expand_path.join("..", "..", "VERSION.txt").read.match(/\A(\d+)\.(\d+)\.(\d+)(\-.*?)?\Z/)
85
+ VERSION = {
86
+ :mayor => $1.to_i,
87
+ :minor => $2.to_i,
88
+ :tiny => $3.to_i,
89
+ :dev => $4,
90
+ }
91
+
92
+ class << self
93
+
94
+ #The temporary directory used by smc-get.
95
+ attr_reader :temp_dir
96
+
97
+ #Initializes the library.
98
+ def setup
99
+ @temp_dir = Pathname.new(Dir.mktmpdir("smc-get"))
100
+ at_exit{@temp_dir.rmtree}
101
+ VERSION.freeze #Nobody changes this after initializing anymore!
102
+ end
103
+
104
+ #Returns smc-get's version by concatenating the VERSION constant's
105
+ #values in a sensible mannor. Return value is a string of form
106
+ # mayor.minor.tiny[-dev|-rc|-beta1|...] (<date>, commit <commit_num>)
107
+ def version
108
+ str = "#{VERSION[:mayor]}.#{VERSION[:minor]}.#{VERSION[:tiny]}"
109
+ str << VERSION[:dev] if VERSION[:dev]
110
+ str
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
117
+ # vim:set ts=8 sts=2 sw=2 et: #
data/smcpak.rdoc ADDED
@@ -0,0 +1,332 @@
1
+ = Secret Maryo Chronicles Package specification
2
+
3
+ This file describes how Secret Maryo Chronicles Packages are defined.
4
+ They allow for easy deployment and uploading to an online repository which
5
+ we have not yet created, but are going to do soon!
6
+
7
+ == Contents
8
+
9
+ 1. General
10
+ 2. File format
11
+ 3. Levels format
12
+ 4. Worlds format
13
+ 5. Graphics format
14
+ 6. Sounds and music format
15
+ 7. README.txt format
16
+ 8. Specification format
17
+ 1. Package specification structure
18
+ 9. About versions and updates
19
+ 10. Repositories
20
+ 1. Repository layout
21
+ 2. packages.lst format
22
+ 3. Repository example
23
+
24
+ == General
25
+
26
+ * The files have the extension <tt>.smcpak</tt>.
27
+ * The files are xz-compressed tarballs.
28
+ * The filename may not contain whitespace.
29
+
30
+ == File format
31
+
32
+ mypackage.smcpak/
33
+ - README.txt
34
+ - mypackage.yml
35
+ levels/
36
+ - Level1.smclvl
37
+ - Level1_sublevel.smclvl
38
+ - Levelsetname_01Level1.smclvl
39
+ - Levelsetname_01Level1_sublevel.smclvl
40
+ worlds/
41
+ world1/
42
+ - description.xml
43
+ - layer.xml
44
+ - world.xml
45
+ pixmaps/
46
+ - graphic1.png
47
+ - graphic2.png
48
+ sounds/
49
+ - sound1.ogg
50
+ - sound2.ogg
51
+ music/
52
+ - music1.ogg
53
+ - music2.ogg
54
+
55
+ == Levels format
56
+
57
+ * A Level must be named after one of the following conventions:
58
+
59
+ [Levelname.smclvl] This is the simplest way to name a level. If your
60
+ package just contains one level or several ones that aren't
61
+ related to each other, use this convention.
62
+ [Level1_sublevel.smclvl] Same as the previous convention, but your levels
63
+ have sublevels. Prefix the sublevel with the
64
+ main level's name and an underscore.
65
+ [Levelsetname_xxLevel1.smclvl] This is the convention you want to use if you
66
+ have multiple levels related to each other, e.g.
67
+ they just follow one another or are combined
68
+ in an overworld. Prefix the levels' names with
69
+ the levelset's name, an underscore, and then
70
+ a 2-digit number indicating in which order
71
+ the levels should be played.
72
+ [Levelsetname_xxLevel1_sublevel.smclvl] The same as the previous convention,
73
+ but the levels have sublevels. Just
74
+ append an underscore to the previous
75
+ convention, then put the sublevel's
76
+ name.
77
+
78
+ * None of the conventions allows for whitespace in the level names. Use
79
+ underscores if necessary.
80
+
81
+ == Worlds format
82
+
83
+ * We don't have any assumptions about how you name your worlds, but
84
+ * do not use whitespace in their names.
85
+
86
+ == Graphics format
87
+
88
+ * Graphics are in the PNG (Portable Network Graphics) format.
89
+ * Their names don't contain whitespace.
90
+ * They may reside in subfolders.
91
+
92
+ == Sounds and music format
93
+
94
+ * They are in OGG (OGG Theora) format.
95
+ * Their names don't contain whitespace.
96
+ * They may reside in subfolders.
97
+
98
+ == README.txt format
99
+
100
+ * The filename is always <tt>README.txt</tt>.
101
+ * Don't offend anybody in your README.
102
+ * Everything else is up to you.
103
+
104
+ == Specification format
105
+
106
+ The package specifiaction is the main file of the package and you should pay close attention to it.
107
+
108
+ * The file is in YAML format (Yet Another Markup Language).
109
+ * The filename is always the same as the package name (*not* as the title,
110
+ i.e. if the title is "My Package" and you name the package
111
+ "mypackage.smcpak", then the spec must be named "mypackage.yml").
112
+
113
+ The general structure is as follows:
114
+
115
+ === Package specification structure
116
+
117
+ ---
118
+ title: "TITLE OF YOUR PACKAGE"
119
+ last_update: 2011-01-23 15:48:00Z
120
+ authors:
121
+ - Author1
122
+ - Author2
123
+ difficulty: "DIFFICULTY"
124
+ dependencies:
125
+ - package1
126
+ - package2
127
+ description: >
128
+ An arbitrary long description of your package that can
129
+ span multiple lines.
130
+ install_message: >
131
+ A message to print to the terminal after the package has been installed.
132
+ It may span multiple lines and you're free to ommit this option at all.
133
+ remove_message: >
134
+ A message to print to the terminal after the package has been removed.
135
+ It may span multiple lines and you're free to ommit this option at all.
136
+ levels:
137
+ - lvl1.smclvl
138
+ - lvl2.smclvl
139
+ graphics:
140
+ - pic1.png
141
+ - pic2.png
142
+ music:
143
+ - music1.ogg
144
+ - music2.ogg
145
+ sounds:
146
+ - sound1.ogg
147
+ - sound2.ogg
148
+ worlds:
149
+ - world1
150
+ - world2
151
+ checksums:
152
+ levels:
153
+ lvl1.smclvl: <checksum>
154
+ lvl2.smclvl: <checksum>
155
+ graphics:
156
+ pic1.png: <checksum>
157
+ pic2.png: <checksum>
158
+ music:
159
+ music1.ogg: <checksum>
160
+ music2.ogg: <checksum>
161
+ sounds:
162
+ sound1.ogg: <checksum>
163
+ sound2.ogg: <checksum>
164
+ worlds:
165
+ world1:
166
+ description.xml: <checksum>
167
+ layer.xml: <checksum>
168
+ world.xml: <checksum>
169
+ world2:
170
+ description.xml: <checksum>
171
+ layer.xml: <checksum>
172
+ world.xml: <checksum>
173
+
174
+ * The +title+ may not be longer than 80 characters. It can contain whitespace.
175
+ This field is mandatory.
176
+ * The +last_update+ field is used by <tt>smc-get</tt> in order to find out weather or
177
+ not a package needs to be updated. Fill this with your last modification time in YAML
178
+ format, preferably using UTC instead of your local timezone which would have the
179
+ following format:
180
+ YYYY-MM-DD hh:mm:ssZ
181
+ The terminating +Z+ is meant literally, i.e. if you’re using UTC it has to be in the
182
+ final format string. See the sample specification above for an example.
183
+ * The list of +authors+ can be arbitrary long, but must contain at least
184
+ one entry. This field is mandatory.
185
+ * The +difficulty+ can be anything you like, but we encourage you to use
186
+ one of the following ones:
187
+ * easy
188
+ * medium
189
+ * hard
190
+ * unknown
191
+ * You *have* to put +difficulty+, even if you just put +unknown+ as the value.
192
+ * The +dependencies+ list contains of an arbitrary long list of package names
193
+ that have to be installed before this package can be installed. Use this
194
+ sparingly, because users don't want to end up with 100 packages be installed
195
+ for a single one. If possible, ommit it at all.
196
+ * The +descripton+ field is mandatory. It contains a multiline description
197
+ of what this package contains.
198
+ * The +install_message+ field is optional. See the above figure for further
199
+ explanation.
200
+ * The +remove_message+ field is optional. See the above figure for further
201
+ explanation.
202
+ * The +levels+, +graphics+, +music+, +sounds+ and +worlds+ fields are all
203
+ optional, but it makes sense to include at least one of them. They state
204
+ which files in your directories actually belong to the package. For each
205
+ option, you specify file paths relative to a directory named the same
206
+ as the option (e.g. files you list under "levels" reside in a "levels"
207
+ directory in your package's root directory). An exception is +worlds+,
208
+ where you specify directory paths rather than file paths.
209
+ * All fields under the +checksums+ field are mandatory. They contain the
210
+ (hex-encoded) SHA1 checksums of all important files in the package.
211
+
212
+ The multiline fields can have two different formats that affect how
213
+ newlines are treated. The first format you already saw is this one:
214
+
215
+ desc: >
216
+ Newlines that are placed here, like the following
217
+ do not show up in the output.
218
+
219
+ Note that the output won't have a newline after the world "following". The
220
+ second form is this one:
221
+
222
+ desc: |
223
+ Newlines that are placed here, like the following
224
+ do show up in the output.
225
+
226
+ This time the output will have a newline after the word "following". We don't
227
+ recommand one form over the other, but keep in mind that using the first
228
+ form may better fit large terminals, whereas the second form allows you
229
+ to prevent cutting words in the middle for line breaking (assuming a
230
+ standard 80-characters wide terminal).
231
+
232
+ == About versions and updates
233
+
234
+ <tt>smc-get</tt> doesn't have a concept of versions. During install,
235
+ <tt>smc-get</tt> writes the installation date into a special file, allowing
236
+ you to alter the levels downloaded without corrupting it's date. Then, when
237
+ you run <tt>smc-get update</tt>, <tt>smc-get</tt> checks wheather the
238
+ modification times of the package files in the repository are newer than
239
+ the installation date it remembered previously. If it finds one or more
240
+ packages that fulfill this requirement, they will be uninstalled and the
241
+ newer package will be downloaded and installed. If <tt>smc-get</tt> detects
242
+ changes have been made to the levels contained in the package, it will
243
+ ask you if you want to lose them by overwriting the levels with the new
244
+ version from the repository, or if the modified level should be moved to
245
+ a new file with the name <tt>levelname.MODIFIED.smclvl</tt>.
246
+
247
+ == Repositories
248
+
249
+ The main repository's URL is not clear as of now (2nd April 2011), but is
250
+ likely to be somewhere on Sourceforge.net. This is the repository
251
+ <tt>smc-get</tt> accesses by default, and you'll find that it contains only
252
+ the compressed packages. If you want to download individual files bypassing
253
+ <tt>smc-get</tt>, you can have a look at the "source repository" at
254
+ https://github.com/Luiji/Secret-Maryo-Chronicles-Contributed-Levels . All
255
+ files contained in that repository are collected and compressed to packages
256
+ by means of an automated process (hopefully) and then uploaded to the main
257
+ repository at sourceforge.
258
+
259
+ === Repository layout
260
+
261
+ A repository's access type is unimportant, <tt>smc-get</tt> will handle
262
+ FTP, HTTP and HTTPS all reasonably well. The directory structure of a repository
263
+ is the thing where you have to pay close attention to, otherwise
264
+ <tt>smc-get</tt> will complain and fail. This is what the URIs should look like:
265
+
266
+ http://your_host.org/smc-repo
267
+ /packages
268
+ /specs
269
+ /packages.lst
270
+
271
+ * The +packages+ sub-uri contains the actual packages, tar-xz files ending in
272
+ <tt>.smcpak</tt>, as discussed earlier in this document.
273
+ * The +specs+ sub-uri contains the package specifications of all the packages
274
+ in the +packages+ dir, uncompressed to faciliate searching for specific
275
+ attributes of a package. They obey the same format as the normal package
276
+ specifications discussed earlier, and in fact, they *are* the same
277
+ specifications.
278
+ * The <tt>packages.lst</tt> file contains a list of all packages the repository
279
+ contains. The format is described below.
280
+
281
+ For instance, if the user wanted <tt>smc-get</tt> to download <tt>mypackage</tt>
282
+ from your repository, <tt>smc-get</tt> would perform the following queries:
283
+
284
+ 1. <tt>GET http://your_host.org/smc-repo/packages.lst</tt>. This is to check if
285
+ the repository even contains the package.
286
+ 2. <tt>GET http://your_host.org/smc-repo/packages/mypackage.smcpak</tt>. This
287
+ is the actual download operation.
288
+
289
+ Or, if the user wants to search for a package whose description contains
290
+ "this is cool" <tt>smc-get</tt> would act like this:
291
+
292
+ 1. <tt>GET http://your_host.org/smc-repo/packages.lst</tt>. This is to get a
293
+ list of all packages the repository contains.
294
+ 2. <tt>GET http://your_host.org/smc-repo/specs/firstspec.yml</tt>. The first
295
+ package's specification mentioned in your <tt>packages.lst</tt> gets
296
+ downloaded. If it contains the search query "this is cool", <tt>smc-get</tt>
297
+ provides the user with some information and then continues with the next
298
+ specification, and so on, until either the user stops the process or all
299
+ package specifications have been queried.
300
+
301
+ === packages.lst format
302
+
303
+ This file is a simple text file that contains one package name per line. For
304
+ example, if your repository contains the packages "mypackage.smcpak" and
305
+ "mygroup/my2ndpackage.smcpak", the file would look like this:
306
+
307
+ mypackage
308
+ mygroup/my2ndpackage
309
+
310
+ Note that the file extension <tt>.smcpak</tt> is ommited.
311
+
312
+ === Repository example
313
+
314
+ Suppose your repository contains the packages "cool_level", "myworld", and
315
+ "incredible_levels/hyper1". The URIs your repository provides should look
316
+ like this:
317
+
318
+ http://your_host.org/smc-repo/packages/cool_level.smcpak
319
+ http://your_host.org/smc-repo/packages/myworld.smcpak
320
+ http://your_host.org/smc-repo/packages/incredible_levels/hyper1.smcpak
321
+
322
+ http://your_host.org/smc-repo/specs/cool_level.yml
323
+ http://your_host.org/smc-repo/specs/myworld.yml
324
+ http://your_host.org/smc-repo/specs/incredible_levels/hyper1.yml
325
+
326
+ http://your_host.org/smc-repo/packages.lst
327
+
328
+ and the <tt>packages.lst</tt> should look like this:
329
+
330
+ cool_level
331
+ myworld
332
+ incredible_levels/hyper1