psdk-cli 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/.idea/plugin/PluginManager.md +65 -0
- data/.solargraph.yml +12 -11
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/exe/psdk-use +7 -0
- data/lib/psdk/cli/configuration.rb +139 -0
- data/lib/psdk/cli/plugin.rb +33 -4
- data/lib/psdk/cli/use.rb +60 -0
- data/lib/psdk/cli/version.rb +1 -1
- data/lib/psdk/cli.rb +15 -2
- data/lib/psdk/helpers/900 Yuki__VD.rb +147 -0
- data/lib/psdk/helpers/plugin_manager/builder.rb +167 -0
- data/lib/psdk/helpers/plugin_manager/config.rb +37 -0
- data/lib/psdk/helpers/plugin_manager/list.rb +49 -0
- data/lib/psdk/helpers/plugin_manager.rb +23 -0
- data/lib/psdk/helpers/psdk.rb +214 -0
- data/lib/psdk/helpers/studio.rb +66 -0
- data/lib/psdk/helpers/version.rb +105 -0
- data/lib/psdk/helpers/version_update.rb +60 -0
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6295fefe865cbe37d27bb3f32e56a66ed1ce9a69501eb18c974101003a961e1d
|
|
4
|
+
data.tar.gz: 7478cdb74640fa0e52aecefce1fc3445cfc6ec78bccd1f5a29263de93550d960
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 477617bf0fbef65be4a5f4c9a3088cd882df1a0abd0169384740ea77c5552b687a6d9a8dde58dab53841e70c7564054f6987ca89b599d1180244c24e23915ab7
|
|
7
|
+
data.tar.gz: c11218c05638142d4f6b7e36ce0620508d424f141791d1466ac089aadb9125aa955766d7d59fdfc9ed7671b6eab37fbc93b10ad38343ecee7f9a5da6926bc205
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
## Story
|
|
2
|
+
|
|
3
|
+
Source: [Github Issue #10](https://github.com/PokemonWorkshop/psdk-cli/issues/10)
|
|
4
|
+
|
|
5
|
+
### Description
|
|
6
|
+
|
|
7
|
+
As a developer, I want to use `psdk-plugin` to manage my PSDK plugins (create, test, update) to simplify maintenance.
|
|
8
|
+
|
|
9
|
+
> [!NOTE]
|
|
10
|
+
> We'll remove the plugin command from PSDK (aside the one for loading the plugin) and psdk cli will be responsible of plugin management (including build).
|
|
11
|
+
> It'll accept both workflow:
|
|
12
|
+
> - Plugin inside a PSDK project (meaning the resources are expected to be at the root of the project and scripts at the root of the plugin)
|
|
13
|
+
> - Plugin outside a PSDK project (meaning both resources and scripts are expected to be at the root of the plugin)
|
|
14
|
+
> The definition file remain the same for both workflow
|
|
15
|
+
|
|
16
|
+
### Acceptance criteria
|
|
17
|
+
|
|
18
|
+
- [ ] `psdk-plugin` exposes the same arguments as the original plugin manager.
|
|
19
|
+
- [ ] The command detects the project or switches to standalone mode.
|
|
20
|
+
- [ ] A clear message lists available actions (list, build).
|
|
21
|
+
|
|
22
|
+
## Information about `PluginManager.rb`
|
|
23
|
+
|
|
24
|
+
This file depends on Yuki::VD (defined in `lib/psdk/helpers/900 Yuki__VD.rb`)
|
|
25
|
+
|
|
26
|
+
This file was taken out of Pokémon SDK and currently is only working in the context of a PSDK project.
|
|
27
|
+
|
|
28
|
+
In the psdk-cli I want the following commands:
|
|
29
|
+
|
|
30
|
+
- `PluginManager.start(:build, 'plugin_name')`
|
|
31
|
+
- `PluginManager.start(:list)`
|
|
32
|
+
|
|
33
|
+
The `PluginManager.start(:load)` should never work in the psdk-cli as it's supposed to run in a game not in the CLI.
|
|
34
|
+
|
|
35
|
+
Currently, the build process expects the following structure:
|
|
36
|
+
|
|
37
|
+
- `scripts/{plugin_name}/config.yml` (plugin configuration file)
|
|
38
|
+
- `scripts/{plugin_name}/script/**/*.rb` (scripts, automatic)
|
|
39
|
+
- `graphics/**/*.*` (added_file)
|
|
40
|
+
- `Data/**/*.*` (added_files)
|
|
41
|
+
- `audio/**/*.*` (added_files)
|
|
42
|
+
|
|
43
|
+
I want this process to be preserved if psdk-cli is running in a PSDK project (`Configuration.project_path != nil`). And if I'm not in a project (or a --no-psdk-project flag is set). I want the following structure so developing plugins is easier:
|
|
44
|
+
|
|
45
|
+
- `config.yml` (plugin configuration file)
|
|
46
|
+
- `scripts/**/*.rb` (scripts, automatic)
|
|
47
|
+
- `graphics/**/*.*` (added_file)
|
|
48
|
+
- `Data/**/*.*` (added_files)
|
|
49
|
+
- `audio/**/*.*` (added_files)
|
|
50
|
+
|
|
51
|
+
In this structure, it's assumed that we are inside `{plugin_name}` (if we use `.` as plugin directory we should skip the plugin_name argument of `PluginManager.start(:build)` since the name is also inside `config.yml`)
|
|
52
|
+
|
|
53
|
+
## Task to perform
|
|
54
|
+
|
|
55
|
+
1. Explode `PluginManager.rb` in several ruby scripts that are able to perform the following operations:
|
|
56
|
+
- `list` all the plugins of current project (should fail if not in a project)
|
|
57
|
+
- `build` a plugin (name is optional, if not provided and not in a project, it's `.`)
|
|
58
|
+
2. Rewrite the Build command so it's
|
|
59
|
+
1. Verbose (explaining each stuff it does and where it is)
|
|
60
|
+
2. Able to work outside of a psdk project (should also state it's not in a PSDK project)
|
|
61
|
+
3. Move the exploded PluginManager files to `lib/psdk/helpers/plugin-manager`
|
|
62
|
+
4. Write a script in `lib/psdk/cli` to expose the `psdk-cli plugin` cli commands
|
|
63
|
+
5. Write a `psdk-plugin` file in `exe` (so I can run `psdk-plugin` instead of `psdk-cli plugin`)
|
|
64
|
+
6. Write unit tests in `spec` for Exploded PluginManager files
|
|
65
|
+
7. Write unit tests in `spec` for the `psdk-cli plugin` commands
|
data/.solargraph.yml
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
include:
|
|
3
|
-
- Rakefile
|
|
4
|
-
- Gemfile
|
|
5
|
-
- "*.gemspec"
|
|
6
|
-
- "**/*.rb"
|
|
3
|
+
- Rakefile
|
|
4
|
+
- Gemfile
|
|
5
|
+
- "*.gemspec"
|
|
6
|
+
- "**/*.rb"
|
|
7
7
|
exclude:
|
|
8
|
-
- spec/**/*
|
|
9
|
-
- test/**/*
|
|
10
|
-
- vendor/**/*
|
|
11
|
-
- ".bundle/**/*"
|
|
8
|
+
- spec/**/*
|
|
9
|
+
- test/**/*
|
|
10
|
+
- vendor/**/*
|
|
11
|
+
- ".bundle/**/*"
|
|
12
12
|
require: []
|
|
13
13
|
domains: []
|
|
14
14
|
reporters:
|
|
15
|
-
- rubocop
|
|
16
|
-
- require_not_found
|
|
15
|
+
- rubocop
|
|
16
|
+
# - require_not_found
|
|
17
17
|
formatter:
|
|
18
18
|
rubocop:
|
|
19
19
|
cops: safe
|
|
@@ -21,5 +21,6 @@ formatter:
|
|
|
21
21
|
only: []
|
|
22
22
|
extra_args: []
|
|
23
23
|
require_paths: []
|
|
24
|
-
plugins:
|
|
24
|
+
plugins:
|
|
25
|
+
- solargraph-rspec
|
|
25
26
|
max_files: 5000
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -12,7 +12,7 @@ gem install psdk-cli
|
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
|
-
In a terminal you can run `psdk-cli` it will list all the available commands.
|
|
15
|
+
In a terminal you can run `psdk-cli` it will list all the available commands.
|
|
16
16
|
|
|
17
17
|
Please note that sub command (such as `psdk-cli plugin`) can be executed using the shortcut `psdk-<sub>` (eg. `psdk-plugin` for `psdk-cli plugin`).
|
|
18
18
|
|
data/exe/psdk-use
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module Psdk
|
|
6
|
+
module Cli
|
|
7
|
+
# Class holding the configuration of the Cli
|
|
8
|
+
class Configuration
|
|
9
|
+
# Filename of the project configuration
|
|
10
|
+
PROJECT_CONFIGURATION_FILENAME = '.psdk-cli.yml'
|
|
11
|
+
|
|
12
|
+
# Path where all the global configuration are stored
|
|
13
|
+
PATH = File.join(ENV.fetch('PSDK_CLI_DIR', Dir.home || ENV['USERPROFILE'] || '~'), '.psdk-cli')
|
|
14
|
+
|
|
15
|
+
# Filename of the global configuration
|
|
16
|
+
GLOBAL_CONFIGURATION_FILENAME = File.join(PATH, 'config.yml')
|
|
17
|
+
|
|
18
|
+
# Create a new configuration
|
|
19
|
+
# @param hash [Hash] configuration hash
|
|
20
|
+
def initialize(hash)
|
|
21
|
+
hash = {} unless hash.is_a?(Hash)
|
|
22
|
+
# @type [String]
|
|
23
|
+
@studio_path = ''
|
|
24
|
+
# @type [Array<String>]
|
|
25
|
+
@project_paths = []
|
|
26
|
+
|
|
27
|
+
self.studio_path = hash[:studio_path] if hash.key?(:studio_path)
|
|
28
|
+
self.project_paths = hash[:project_paths] if hash.key?(:project_paths)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get the Pokémon Studio path
|
|
32
|
+
# @return [String]
|
|
33
|
+
attr_reader :studio_path
|
|
34
|
+
|
|
35
|
+
# Set the Pokémon Studio path
|
|
36
|
+
# @param path [String]
|
|
37
|
+
def studio_path=(path)
|
|
38
|
+
unless Dir.exist?(path) || path.empty?
|
|
39
|
+
puts "[Error] Invalid studio_path at `#{path}`, this path does not exists"
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
# TODO: add check for locating psdk-binaries
|
|
43
|
+
@studio_path = path
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get the project paths
|
|
47
|
+
# @return [Array<String>]
|
|
48
|
+
attr_reader :project_paths
|
|
49
|
+
|
|
50
|
+
# Set the project_paths
|
|
51
|
+
# @param paths [Array<String>]
|
|
52
|
+
def project_paths=(paths)
|
|
53
|
+
unless paths.is_a?(Array)
|
|
54
|
+
puts '[Error] project_paths is not an array'
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
unless paths.all? { |path| path.is_a?(String) }
|
|
58
|
+
puts '[Error] some of the project paths are not path'
|
|
59
|
+
return
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@project_paths = paths
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def to_h
|
|
66
|
+
return {
|
|
67
|
+
studio_path: @studio_path,
|
|
68
|
+
project_paths: @project_paths
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class << self
|
|
73
|
+
@global = nil
|
|
74
|
+
@local = nil
|
|
75
|
+
|
|
76
|
+
# Get the project path
|
|
77
|
+
# @return [String | nil]
|
|
78
|
+
attr_reader :project_path
|
|
79
|
+
|
|
80
|
+
# Get the configuration
|
|
81
|
+
# @param type [:global | :local]
|
|
82
|
+
# @return [Configuration]
|
|
83
|
+
def get(type)
|
|
84
|
+
@global ||= Configuration.new(load_hash(GLOBAL_CONFIGURATION_FILENAME))
|
|
85
|
+
return @global if type == :global
|
|
86
|
+
|
|
87
|
+
project_path = find_project_path
|
|
88
|
+
@local = nil if @project_path != project_path
|
|
89
|
+
@project_path = project_path
|
|
90
|
+
@local ||= Configuration.new(
|
|
91
|
+
@global.to_h.merge(load_hash(File.join(*@project_path, PROJECT_CONFIGURATION_FILENAME)))
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return @local
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Save the configuration
|
|
98
|
+
def save
|
|
99
|
+
make_config_global_dir
|
|
100
|
+
File.write(GLOBAL_CONFIGURATION_FILENAME, YAML.dump(@global.to_h))
|
|
101
|
+
return unless @local && @project_path
|
|
102
|
+
|
|
103
|
+
local_configuration = @local.to_h
|
|
104
|
+
# Delete global configuration keys
|
|
105
|
+
local_configuration.delete(:project_paths)
|
|
106
|
+
|
|
107
|
+
File.write(File.join(@project_path, PROJECT_CONFIGURATION_FILENAME), YAML.dump(local_configuration))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def make_config_global_dir
|
|
113
|
+
dirname = File.dirname(GLOBAL_CONFIGURATION_FILENAME)
|
|
114
|
+
return if Dir.exist?(dirname)
|
|
115
|
+
|
|
116
|
+
Dir.mkdir(dirname)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def load_hash(path)
|
|
120
|
+
return {} unless File.exist?(path)
|
|
121
|
+
|
|
122
|
+
return YAML.load_file(path, symbolize_names: true, freeze: true)
|
|
123
|
+
rescue StandardError => e
|
|
124
|
+
puts "[Error] failed to load configuration (#{e.message})"
|
|
125
|
+
puts e.backtrace
|
|
126
|
+
return {}
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Get the project path
|
|
130
|
+
# @return [String | nil]
|
|
131
|
+
def find_project_path
|
|
132
|
+
current_path = Dir.pwd.gsub('\\', '/').split('/')
|
|
133
|
+
all_options = current_path.size.downto(2).map { |i| File.join(*current_path[0...i]) }
|
|
134
|
+
return all_options.find { |path| File.exist?(File.join(path, 'project.studio')) }
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
data/lib/psdk/cli/plugin.rb
CHANGED
|
@@ -8,10 +8,39 @@ module Psdk
|
|
|
8
8
|
class Plugin < Thor
|
|
9
9
|
package_name 'psdk-plugin'
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
desc 'list', 'List all installed plugins in the current PSDK project'
|
|
12
|
+
def list
|
|
13
|
+
require_relative 'configuration'
|
|
14
|
+
require_relative '../helpers/plugin_manager'
|
|
15
|
+
Psdk::Cli::Configuration.get(:local)
|
|
16
|
+
|
|
17
|
+
unless Psdk::Cli::Configuration.project_path
|
|
18
|
+
puts 'Error: You must be inside a PSDK project to use `psdk-plugin list`.'
|
|
19
|
+
exit(1)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
PluginManager.list
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
desc 'build [PLUGIN_NAME]', 'Build a plugin (requires to be at the root of the project or the plugin)'
|
|
26
|
+
option :no_psdk_project, type: :boolean, desc: 'Force standalone mode (build outside a PSDK project)'
|
|
27
|
+
option :out_dir, type: :string, default: '.', desc: 'Output directory for the generated .psdkplug file'
|
|
28
|
+
def build(plugin_name = nil) # rubocop:disable Metrics/MethodLength
|
|
29
|
+
require_relative 'configuration'
|
|
30
|
+
require_relative '../helpers/plugin_manager'
|
|
31
|
+
Psdk::Cli::Configuration.get(:local)
|
|
32
|
+
|
|
33
|
+
in_project = Psdk::Cli::Configuration.project_path
|
|
34
|
+
in_project = false if options[:no_psdk_project]
|
|
35
|
+
|
|
36
|
+
plugin_name = '.' if !in_project && plugin_name.nil?
|
|
37
|
+
|
|
38
|
+
unless plugin_name
|
|
39
|
+
puts 'Error: You must provide a plugin_name when building inside a PSDK project.'
|
|
40
|
+
exit(1)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
PluginManager.build(plugin_name, in_project: in_project, out_dir: options[:out_dir])
|
|
15
44
|
end
|
|
16
45
|
end
|
|
17
46
|
end
|
data/lib/psdk/cli/use.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'thor'
|
|
4
|
+
|
|
5
|
+
module Psdk
|
|
6
|
+
module Cli
|
|
7
|
+
# Class responsible of handling the psdk-use cli commands
|
|
8
|
+
class Use < Thor
|
|
9
|
+
package_name 'psdk-use'
|
|
10
|
+
|
|
11
|
+
desc 'studio [--delete]', 'make the project use Pokemon Studio PSDK version.'
|
|
12
|
+
method_option :delete, type: :boolean, aliases: '--delete',
|
|
13
|
+
desc: 'delete local pokemonsdk folder'
|
|
14
|
+
def studio
|
|
15
|
+
ensure_project
|
|
16
|
+
require_relative '../helpers/psdk'
|
|
17
|
+
PSDK.unuse_local_pokemonsdk(delete: options[:delete])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
desc 'version [PSDK_VERSION]', 'make the project use a specific PSDK version'
|
|
21
|
+
def version(psdk_version)
|
|
22
|
+
ensure_project
|
|
23
|
+
require_relative '../helpers/psdk'
|
|
24
|
+
PSDK.use_version(psdk_version)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc 'commit [SHA1]', 'make the project use a specific PSDK commit'
|
|
28
|
+
def commit(sha1)
|
|
29
|
+
ensure_project
|
|
30
|
+
require_relative '../helpers/psdk'
|
|
31
|
+
PSDK.use_commit(sha1)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc 'mr [ID]', 'make the project use a specific MR'
|
|
35
|
+
def mr(id)
|
|
36
|
+
ensure_project
|
|
37
|
+
require_relative '../helpers/psdk'
|
|
38
|
+
PSDK.use_mr(id)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc 'latest', 'make the project use the latest PSDK commit from development'
|
|
42
|
+
def latest
|
|
43
|
+
ensure_project
|
|
44
|
+
require_relative '../helpers/psdk'
|
|
45
|
+
PSDK.use_latest
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def ensure_project
|
|
51
|
+
require_relative 'configuration'
|
|
52
|
+
Configuration.get(:local)
|
|
53
|
+
return if Configuration.project_path
|
|
54
|
+
|
|
55
|
+
$stderr.puts 'Not in a project'
|
|
56
|
+
exit(1)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/psdk/cli/version.rb
CHANGED
data/lib/psdk/cli.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'thor'
|
|
4
4
|
require_relative 'cli/version'
|
|
5
5
|
require_relative 'cli/plugin'
|
|
6
|
+
require_relative 'cli/use'
|
|
6
7
|
|
|
7
8
|
module Psdk
|
|
8
9
|
module Cli
|
|
@@ -12,13 +13,25 @@ module Psdk
|
|
|
12
13
|
class Main < Thor
|
|
13
14
|
package_name 'psdk-cli'
|
|
14
15
|
|
|
15
|
-
desc
|
|
16
|
+
desc 'version', 'show the psdk-cli version'
|
|
17
|
+
method_option :no_psdk_version, type: :boolean, aliases: '--no-psdk-version',
|
|
18
|
+
desc: 'do not search and show PSDK version'
|
|
16
19
|
def version
|
|
17
|
-
|
|
20
|
+
require_relative 'helpers/version'
|
|
21
|
+
Version.run(options[:no_psdk_version])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
desc 'update', 'update the psdk-cli'
|
|
25
|
+
def update
|
|
26
|
+
require_relative 'helpers/version_update'
|
|
27
|
+
VersionUpdate.check_and_update
|
|
18
28
|
end
|
|
19
29
|
|
|
20
30
|
desc 'plugin', 'manage PSDK plugins'
|
|
21
31
|
subcommand 'plugin', Plugin
|
|
32
|
+
|
|
33
|
+
desc 'use', 'manage project\'s PSDK version'
|
|
34
|
+
subcommand 'use', Use
|
|
22
35
|
end
|
|
23
36
|
end
|
|
24
37
|
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# rubocop:disable all
|
|
2
|
+
module Yuki
|
|
3
|
+
# Class that helps to read Virtual Directories
|
|
4
|
+
#
|
|
5
|
+
# In reading mode, the Virtual Directories can be loaded to RAM if MAX_SIZE >= VD.size
|
|
6
|
+
#
|
|
7
|
+
# All the filenames inside the Yuki::VD has to be downcased filename in utf-8
|
|
8
|
+
#
|
|
9
|
+
# Note : Encryption is up to the developper and no longer supported on the basic script
|
|
10
|
+
class VD
|
|
11
|
+
# @return [String] the filename of the current Yuki::VD
|
|
12
|
+
attr_reader :filename
|
|
13
|
+
|
|
14
|
+
# Is the debug info on ?
|
|
15
|
+
DEBUG_ON = ARGV.include?('debug-yuki-vd')
|
|
16
|
+
# The max size of the file that can be loaded in memory
|
|
17
|
+
MAX_SIZE = 10 * 1024 * 1024 # 10Mo
|
|
18
|
+
# List of allowed modes
|
|
19
|
+
ALLOWED_MODES = %i[read write update]
|
|
20
|
+
# Size of the pointer at the begin of the file
|
|
21
|
+
POINTER_SIZE = 4
|
|
22
|
+
# Unpack method of the pointer at the begin of the file
|
|
23
|
+
UNPACK_METHOD = 'L'
|
|
24
|
+
# Create a new Yuki::VD file or load it
|
|
25
|
+
# @param filename [String] name of the Yuki::VD file
|
|
26
|
+
# @param mode [:read, :write, :update] if we read or write the virtual directory
|
|
27
|
+
def initialize(filename, mode)
|
|
28
|
+
@mode = mode = fix_mode(mode)
|
|
29
|
+
@filename = filename
|
|
30
|
+
send("initialize_#{mode}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Read a file data from the VD
|
|
34
|
+
# @param filename [String] the file we want to read its data
|
|
35
|
+
# @return [String, nil] the data of the file
|
|
36
|
+
def read_data(filename)
|
|
37
|
+
return nil unless @file
|
|
38
|
+
|
|
39
|
+
pos = @hash[filename]
|
|
40
|
+
return nil unless pos
|
|
41
|
+
|
|
42
|
+
@file.pos = pos
|
|
43
|
+
size = @file.read(POINTER_SIZE).unpack1(UNPACK_METHOD)
|
|
44
|
+
return @file.read(size)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Test if a file exists in the VD
|
|
48
|
+
# @param filename [String]
|
|
49
|
+
# @return [Boolean]
|
|
50
|
+
def exists?(filename)
|
|
51
|
+
@hash[filename] != nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Write a file with its data in the VD
|
|
55
|
+
# @param filename [String] the file name
|
|
56
|
+
# @param data [String] the data of the file
|
|
57
|
+
def write_data(filename, data)
|
|
58
|
+
return unless @file
|
|
59
|
+
|
|
60
|
+
@hash[filename] = @file.pos
|
|
61
|
+
@file.write([data.bytesize].pack(UNPACK_METHOD))
|
|
62
|
+
@file.write(data)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Add a file to the Yuki::VD
|
|
66
|
+
# @param filename [String] the file name
|
|
67
|
+
# @param ext_name [String, nil] the file extension
|
|
68
|
+
def add_file(filename, ext_name = nil)
|
|
69
|
+
sub_filename = ext_name ? "#{filename}.#{ext_name}" : filename
|
|
70
|
+
write_data(filename, File.binread(sub_filename))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Get all the filename
|
|
74
|
+
# @return [Array<String>]
|
|
75
|
+
def get_filenames
|
|
76
|
+
@hash.keys
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Close the VD
|
|
80
|
+
def close
|
|
81
|
+
return unless @file
|
|
82
|
+
|
|
83
|
+
if @mode != :read
|
|
84
|
+
pos = [@file.pos].pack(UNPACK_METHOD)
|
|
85
|
+
@file.write(Marshal.dump(@hash))
|
|
86
|
+
@file.pos = 0
|
|
87
|
+
@file.write(pos)
|
|
88
|
+
end
|
|
89
|
+
@file.close
|
|
90
|
+
@file = nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
# Initialize the Yuki::VD in read mode
|
|
96
|
+
def initialize_read
|
|
97
|
+
@file = File.new(filename, 'rb')
|
|
98
|
+
pos = @file.pos = @file.read(POINTER_SIZE).unpack1(UNPACK_METHOD)
|
|
99
|
+
@hash = Marshal.load(@file)
|
|
100
|
+
load_whole_file(pos) if pos < MAX_SIZE
|
|
101
|
+
rescue Errno::ENOENT
|
|
102
|
+
@file = nil
|
|
103
|
+
@hash = {}
|
|
104
|
+
log_error(format('%<filename>s not found', filename: filename)) if DEBUG_ON
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Load the VD in the memory
|
|
108
|
+
# @param size [Integer] size of the VD memory
|
|
109
|
+
def load_whole_file(size)
|
|
110
|
+
@file.pos = 0
|
|
111
|
+
data = @file.read(size)
|
|
112
|
+
@file.close
|
|
113
|
+
@file = StringIO.new(data, 'rb+')
|
|
114
|
+
@file.pos = 0
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Initialize the Yuki::VD in write mode
|
|
118
|
+
def initialize_write
|
|
119
|
+
@file = File.new(filename, 'wb')
|
|
120
|
+
@file.pos = POINTER_SIZE
|
|
121
|
+
@hash = {}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Initialize the Yuki::VD in update mode
|
|
125
|
+
def initialize_update
|
|
126
|
+
@file = File.new(filename, 'rb+')
|
|
127
|
+
pos = @file.pos = @file.read(POINTER_SIZE).unpack1(UNPACK_METHOD)
|
|
128
|
+
@hash = Marshal.load(@file)
|
|
129
|
+
@file.pos = pos
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Fix the input mode in case it's a String
|
|
133
|
+
# @param mode [Symbol, String]
|
|
134
|
+
# @return [Symbol] one of the value of ALLOWED_MODES
|
|
135
|
+
def fix_mode(mode)
|
|
136
|
+
return mode if ALLOWED_MODES.include?(mode)
|
|
137
|
+
|
|
138
|
+
r = (mode = mode.downcase).include?('r')
|
|
139
|
+
w = mode.include?('w')
|
|
140
|
+
plus = mode.include?('+')
|
|
141
|
+
return :update if plus || (r && w)
|
|
142
|
+
return :read if r
|
|
143
|
+
|
|
144
|
+
return :write
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|