smc-get 0.1.0 → 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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