wow_dbc 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5eb07f8d26336f262320b25aab7b855e1054a650e0ff608f938a7cd9075a3d1e
4
+ data.tar.gz: fdb1049503d8ec18d1508fc6f0ec88510a6db8624432143430cf4f62453a75b0
5
+ SHA512:
6
+ metadata.gz: b0db030d3fd74442621189f5bc02490b6431c0f05210e058a0a663f642df16b80c7b9a416d540431598117fd0c3cf706d89ce663f55b20b1b2b0535f97e562da
7
+ data.tar.gz: d09f892af804f48e0cfe01077c1c30791a8fb3c761ed6e7f9cd7268b67680fc7c86c5bf48689c1200c5a3efdc1482b3d8307fa5e29e08e819b2504c81f77512b
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ inherit_gem:
2
+ rubocop-rails_config:
3
+ - config/rails.yml
4
+
5
+ Style/ClassAndModuleChildren:
6
+ EnforcedStyle: nested
7
+
8
+ Lint/Debugger:
9
+ Enabled: true
10
+
11
+ Style/StringLiterals:
12
+ Enabled: true
13
+ EnforcedStyle: single_quotes
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-09-22
4
+
5
+ - Initial release
@@ -0,0 +1,132 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official email address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ [INSERT CONTACT METHOD].
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series of
86
+ actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or permanent
93
+ ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within the
113
+ community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.1, available at
119
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
+
121
+ Community Impact Guidelines were inspired by
122
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
+
124
+ For answers to common questions about this code of conduct, see the FAQ at
125
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126
+ [https://www.contributor-covenant.org/translations][translations].
127
+
128
+ [homepage]: https://www.contributor-covenant.org
129
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130
+ [Mozilla CoC]: https://github.com/mozilla/diversity
131
+ [FAQ]: https://www.contributor-covenant.org/faq
132
+ [translations]: https://www.contributor-covenant.org/translations
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 sebi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # WowDBC 🎮
2
+
3
+ WowDBC is a high-performance Ruby gem for reading and manipulating World of Warcraft DBC (Database Client) files. 🚀
4
+
5
+ ## Features 🌟
6
+
7
+ - Fast reading and writing of DBC files
8
+ - CRUD operations for DBC records
9
+ - Ruby-friendly interface with field name access
10
+ - Efficient C extension for optimal performance
11
+
12
+ ## Installation 💎
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'wow_dbc'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```bash
23
+ $ bundle install
24
+ ```
25
+
26
+ Or install it yourself as:
27
+
28
+ ```bash
29
+ $ gem install wow_dbc
30
+ ```
31
+
32
+ ## Usage 📚
33
+
34
+ Here's a quick example of how to use WowDBC:
35
+
36
+ ```ruby
37
+ require 'wow_dbc'
38
+
39
+ # Define field names for your DBC file
40
+ field_names = [:id, :name, :description, :icon, :category, :subcategory]
41
+
42
+ # Open a DBC file
43
+ dbc = WowDBC::DBCFile.new('path/to/your/file.dbc', field_names)
44
+ dbc.read
45
+
46
+ # Read a record
47
+ record = dbc.get_record(0)
48
+ puts "First record: #{record}"
49
+
50
+ # Update a single field in a record
51
+ dbc.update_record(0, :name, "New Name")
52
+
53
+ # Update multiple fields in a record
54
+ dbc.update_record_multi(0, { name: "Newer Name", category: 5, subcategory: 10 })
55
+
56
+ # Create a new empty record
57
+ new_record_index = dbc.create_record
58
+ puts "New empty record index: #{new_record_index}"
59
+
60
+ # Create a new record with initial values
61
+ initial_values = { id: 1000, name: "New Item", category: 3, subcategory: 7 }
62
+ new_record_with_values_index = dbc.create_record_with_values(initial_values)
63
+ puts "New record with values index: #{new_record_with_values_index}"
64
+
65
+ # Read the newly created record
66
+ new_record = dbc.get_record(new_record_with_values_index)
67
+ puts "Newly created record: #{new_record}"
68
+
69
+ # Delete a record
70
+ dbc.delete_record(new_record_index)
71
+
72
+ # Write changes back to the file
73
+ dbc.write
74
+
75
+ # Reading header information
76
+ header = dbc.header
77
+ puts "Total records: #{header[:record_count]}"
78
+ puts "Fields per record: #{header[:field_count]}"
79
+
80
+ # Iterating through all records
81
+ (0...header[:record_count]).each do |i|
82
+ record = dbc.get_record(i)
83
+ puts "Record #{i}: #{record}"
84
+ end
85
+ ```
86
+
87
+ ## Development 🛠️
88
+
89
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
90
+
91
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
92
+
93
+ ## Contributing 🤝
94
+
95
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sebyx07/wow_dbc. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/sebyx07/wow_dbc/blob/master/CODE_OF_CONDUCT.md).
96
+
97
+ 1. Fork it ( https://github.com/sebyx07/wow_dbc/fork )
98
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
99
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
100
+ 4. Push to the branch (`git push origin my-new-feature`)
101
+ 5. Create a new Pull Request
102
+
103
+ ## License 📄
104
+
105
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
106
+
107
+ ## Code of Conduct 🤝
108
+
109
+ Everyone interacting in the WowDBC project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/sebyx07/wow_dbc/blob/master/CODE_OF_CONDUCT.md).
110
+
111
+ ## Acknowledgments 👏
112
+
113
+ - Thanks to all contributors who have helped shape this project.
114
+ - Inspired by the World of Warcraft modding community.
115
+
116
+ Happy coding, and may your adventures in Azeroth be bug-free! 🐉✨
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rake/extensiontask'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ Rake::ExtensionTask.new('wow_dbc') do |ext|
10
+ ext.lib_dir = 'lib/wow_dbc'
11
+ end
12
+
13
+ task default: [:compile, :spec]
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mkmf'
4
+
5
+ create_makefile('wow_dbc/wow_dbc')
@@ -0,0 +1,327 @@
1
+ #include <ruby.h>
2
+ #include <stdio.h>
3
+ #include <stdint.h>
4
+ #include <string.h>
5
+
6
+ typedef struct {
7
+ char magic[4];
8
+ uint32_t record_count;
9
+ uint32_t field_count;
10
+ uint32_t record_size;
11
+ uint32_t string_block_size;
12
+ } DBCHeader;
13
+
14
+ typedef struct {
15
+ DBCHeader header;
16
+ uint32_t **records;
17
+ char *string_block;
18
+ VALUE field_names; // Ruby array of field names
19
+ } DBCFile;
20
+
21
+ static VALUE rb_mWowDBC;
22
+ static VALUE rb_cDBCFile;
23
+
24
+ static void dbc_free(void *ptr) {
25
+ DBCFile *dbc = (DBCFile *)ptr;
26
+ if (dbc->records) {
27
+ for (uint32_t i = 0; i < dbc->header.record_count; i++) {
28
+ free(dbc->records[i]);
29
+ }
30
+ free(dbc->records);
31
+ }
32
+ if (dbc->string_block) {
33
+ free(dbc->string_block);
34
+ }
35
+ free(dbc);
36
+ }
37
+
38
+ static size_t dbc_memsize(const void *ptr) {
39
+ const DBCFile *dbc = (const DBCFile *)ptr;
40
+ return sizeof(DBCFile) +
41
+ (dbc->header.record_count * sizeof(uint32_t *)) +
42
+ (dbc->header.record_count * dbc->header.field_count * sizeof(uint32_t)) +
43
+ dbc->header.string_block_size;
44
+ }
45
+
46
+ static const rb_data_type_t dbc_data_type = {
47
+ "WowDBC::DBCFile",
48
+ {NULL, dbc_free, dbc_memsize,},
49
+ 0, 0,
50
+ RUBY_TYPED_FREE_IMMEDIATELY,
51
+ };
52
+
53
+ static VALUE dbc_alloc(VALUE klass) {
54
+ DBCFile *dbc = ALLOC(DBCFile);
55
+ memset(dbc, 0, sizeof(DBCFile));
56
+ return TypedData_Wrap_Struct(klass, &dbc_data_type, dbc);
57
+ }
58
+
59
+ static VALUE dbc_initialize(VALUE self, VALUE filepath, VALUE field_names) {
60
+ DBCFile *dbc;
61
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
62
+
63
+ rb_iv_set(self, "@filepath", filepath);
64
+ dbc->field_names = rb_ary_dup(field_names);
65
+ rb_iv_set(self, "@field_names", dbc->field_names);
66
+
67
+ return self;
68
+ }
69
+
70
+ static VALUE dbc_read(VALUE self) {
71
+ DBCFile *dbc;
72
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
73
+
74
+ VALUE filepath = rb_iv_get(self, "@filepath");
75
+ FILE *file = fopen(StringValueCStr(filepath), "rb");
76
+ if (!file) {
77
+ rb_raise(rb_eIOError, "Could not open file");
78
+ }
79
+
80
+ if (fread(&dbc->header, sizeof(DBCHeader), 1, file) != 1) {
81
+ fclose(file);
82
+ rb_raise(rb_eIOError, "Failed to read DBC header");
83
+ }
84
+
85
+ // Free existing records if any
86
+ if (dbc->records) {
87
+ for (uint32_t i = 0; i < dbc->header.record_count; i++) {
88
+ free(dbc->records[i]);
89
+ }
90
+ free(dbc->records);
91
+ }
92
+
93
+ dbc->records = ALLOC_N(uint32_t *, dbc->header.record_count);
94
+ for (uint32_t i = 0; i < dbc->header.record_count; i++) {
95
+ dbc->records[i] = ALLOC_N(uint32_t, dbc->header.field_count);
96
+ if (fread(dbc->records[i], sizeof(uint32_t), dbc->header.field_count, file) != dbc->header.field_count) {
97
+ fclose(file);
98
+ rb_raise(rb_eIOError, "Failed to read DBC record");
99
+ }
100
+ }
101
+
102
+ // Free existing string block if any
103
+ if (dbc->string_block) {
104
+ free(dbc->string_block);
105
+ }
106
+
107
+ dbc->string_block = ALLOC_N(char, dbc->header.string_block_size);
108
+ if (fread(dbc->string_block, 1, dbc->header.string_block_size, file) != dbc->header.string_block_size) {
109
+ fclose(file);
110
+ rb_raise(rb_eIOError, "Failed to read DBC string block");
111
+ }
112
+
113
+ fclose(file);
114
+ return self;
115
+ }
116
+
117
+ static VALUE dbc_write(VALUE self) {
118
+ DBCFile *dbc;
119
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
120
+
121
+ VALUE filepath = rb_iv_get(self, "@filepath");
122
+ FILE *file = fopen(StringValueCStr(filepath), "wb");
123
+ if (!file) {
124
+ rb_raise(rb_eIOError, "Could not open file for writing");
125
+ }
126
+
127
+ if (fwrite(&dbc->header, sizeof(DBCHeader), 1, file) != 1) {
128
+ fclose(file);
129
+ rb_raise(rb_eIOError, "Failed to write DBC header");
130
+ }
131
+
132
+ for (uint32_t i = 0; i < dbc->header.record_count; i++) {
133
+ if (fwrite(dbc->records[i], sizeof(uint32_t), dbc->header.field_count, file) != dbc->header.field_count) {
134
+ fclose(file);
135
+ rb_raise(rb_eIOError, "Failed to write DBC record");
136
+ }
137
+ }
138
+
139
+ if (fwrite(dbc->string_block, 1, dbc->header.string_block_size, file) != dbc->header.string_block_size) {
140
+ fclose(file);
141
+ rb_raise(rb_eIOError, "Failed to write DBC string block");
142
+ }
143
+
144
+ fclose(file);
145
+ return self;
146
+ }
147
+
148
+ static VALUE dbc_create_record(VALUE self) {
149
+ DBCFile *dbc;
150
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
151
+
152
+ uint32_t new_count = dbc->header.record_count + 1;
153
+ REALLOC_N(dbc->records, uint32_t *, new_count);
154
+ dbc->records[new_count - 1] = ALLOC_N(uint32_t, dbc->header.field_count);
155
+ memset(dbc->records[new_count - 1], 0, dbc->header.field_count * sizeof(uint32_t));
156
+
157
+ dbc->header.record_count = new_count;
158
+
159
+ return INT2FIX(new_count - 1);
160
+ }
161
+
162
+ static VALUE dbc_update_record(VALUE self, VALUE index, VALUE field_name, VALUE value) {
163
+ DBCFile *dbc;
164
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
165
+
166
+ long idx = FIX2LONG(index);
167
+ long field_idx = -1;
168
+
169
+ // Find the index of the field name
170
+ for (long i = 0; i < RARRAY_LEN(dbc->field_names); i++) {
171
+ if (rb_eql(rb_ary_entry(dbc->field_names, i), field_name)) {
172
+ field_idx = i;
173
+ break;
174
+ }
175
+ }
176
+
177
+ if (idx < 0 || (uint32_t)idx >= dbc->header.record_count || field_idx < 0 || (uint32_t)field_idx >= dbc->header.field_count) {
178
+ rb_raise(rb_eArgError, "Invalid record or field index");
179
+ }
180
+
181
+ dbc->records[idx][field_idx] = NUM2UINT(value);
182
+
183
+ return Qnil;
184
+ }
185
+
186
+ static VALUE dbc_delete_record(VALUE self, VALUE index) {
187
+ DBCFile *dbc;
188
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
189
+
190
+ long idx = FIX2LONG(index);
191
+
192
+ if (idx < 0 || (uint32_t)idx >= dbc->header.record_count) {
193
+ rb_raise(rb_eArgError, "Invalid record index");
194
+ }
195
+
196
+ free(dbc->records[idx]);
197
+ memmove(&dbc->records[idx], &dbc->records[idx + 1], (dbc->header.record_count - idx - 1) * sizeof(uint32_t *));
198
+ dbc->header.record_count--;
199
+
200
+ return Qnil;
201
+ }
202
+
203
+ static VALUE dbc_get_record(VALUE self, VALUE index) {
204
+ DBCFile *dbc;
205
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
206
+
207
+ long idx = FIX2LONG(index);
208
+
209
+ if (idx < 0 || (uint32_t)idx >= dbc->header.record_count) {
210
+ rb_raise(rb_eArgError, "Invalid record index");
211
+ }
212
+
213
+ VALUE record = rb_hash_new();
214
+ for (uint32_t i = 0; i < dbc->header.field_count; i++) {
215
+ VALUE field_name = rb_ary_entry(dbc->field_names, i);
216
+ rb_hash_aset(record, field_name, UINT2NUM(dbc->records[idx][i]));
217
+ }
218
+
219
+ return record;
220
+ }
221
+
222
+ static VALUE dbc_get_header(VALUE self) {
223
+ DBCFile *dbc;
224
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
225
+
226
+ VALUE header = rb_hash_new();
227
+ rb_hash_aset(header, ID2SYM(rb_intern("magic")), rb_str_new(dbc->header.magic, 4));
228
+ rb_hash_aset(header, ID2SYM(rb_intern("record_count")), UINT2NUM(dbc->header.record_count));
229
+ rb_hash_aset(header, ID2SYM(rb_intern("field_count")), UINT2NUM(dbc->header.field_count));
230
+ rb_hash_aset(header, ID2SYM(rb_intern("record_size")), UINT2NUM(dbc->header.record_size));
231
+ rb_hash_aset(header, ID2SYM(rb_intern("string_block_size")), UINT2NUM(dbc->header.string_block_size));
232
+
233
+ return header;
234
+ }
235
+
236
+ static VALUE dbc_create_record_with_values(VALUE self, VALUE initial_values) {
237
+ DBCFile *dbc;
238
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
239
+
240
+ uint32_t new_count = dbc->header.record_count + 1;
241
+ REALLOC_N(dbc->records, uint32_t *, new_count);
242
+ dbc->records[new_count - 1] = ALLOC_N(uint32_t, dbc->header.field_count);
243
+ memset(dbc->records[new_count - 1], 0, dbc->header.field_count * sizeof(uint32_t));
244
+
245
+ // Set initial values
246
+ if (RB_TYPE_P(initial_values, T_HASH)) {
247
+ VALUE keys = rb_funcall(initial_values, rb_intern("keys"), 0);
248
+ for (long i = 0; i < RARRAY_LEN(keys); i++) {
249
+ VALUE key = rb_ary_entry(keys, i);
250
+ VALUE value = rb_hash_aref(initial_values, key);
251
+ long field_idx = -1;
252
+
253
+ for (long j = 0; j < RARRAY_LEN(dbc->field_names); j++) {
254
+ if (rb_eql(rb_ary_entry(dbc->field_names, j), key)) {
255
+ field_idx = j;
256
+ break;
257
+ }
258
+ }
259
+
260
+ if (field_idx >= 0 && (uint32_t)field_idx < dbc->header.field_count) {
261
+ dbc->records[new_count - 1][field_idx] = NUM2UINT(value);
262
+ } else {
263
+ // Free the allocated memory before raising the error
264
+ free(dbc->records[new_count - 1]);
265
+ REALLOC_N(dbc->records, uint32_t *, dbc->header.record_count);
266
+ rb_raise(rb_eArgError, "Invalid field name: %s", rb_id2name(SYM2ID(key)));
267
+ }
268
+ }
269
+ }
270
+
271
+ dbc->header.record_count = new_count;
272
+
273
+ return INT2FIX(new_count - 1);
274
+ }
275
+
276
+ static VALUE dbc_update_record_multi(VALUE self, VALUE index, VALUE updates) {
277
+ DBCFile *dbc;
278
+ TypedData_Get_Struct(self, DBCFile, &dbc_data_type, dbc);
279
+
280
+ long idx = FIX2LONG(index);
281
+
282
+ if (idx < 0 || (uint32_t)idx >= dbc->header.record_count) {
283
+ rb_raise(rb_eArgError, "Invalid record index");
284
+ }
285
+
286
+ if (RB_TYPE_P(updates, T_HASH)) {
287
+ VALUE keys = rb_funcall(updates, rb_intern("keys"), 0);
288
+ for (long i = 0; i < RARRAY_LEN(keys); i++) {
289
+ VALUE key = rb_ary_entry(keys, i);
290
+ VALUE value = rb_hash_aref(updates, key);
291
+ long field_idx = -1;
292
+
293
+ for (long j = 0; j < RARRAY_LEN(dbc->field_names); j++) {
294
+ if (rb_eql(rb_ary_entry(dbc->field_names, j), key)) {
295
+ field_idx = j;
296
+ break;
297
+ }
298
+ }
299
+
300
+ if (field_idx >= 0 && (uint32_t)field_idx < dbc->header.field_count) {
301
+ dbc->records[idx][field_idx] = NUM2UINT(value);
302
+ } else {
303
+ rb_raise(rb_eArgError, "Invalid field name: %s", rb_id2name(SYM2ID(key)));
304
+ }
305
+ }
306
+ } else {
307
+ rb_raise(rb_eArgError, "Updates must be a hash");
308
+ }
309
+
310
+ return Qnil;
311
+ }
312
+
313
+ void Init_wow_dbc(void) {
314
+ rb_mWowDBC = rb_define_module("WowDBC");
315
+ rb_cDBCFile = rb_define_class_under(rb_mWowDBC, "DBCFile", rb_cObject);
316
+ rb_define_alloc_func(rb_cDBCFile, dbc_alloc);
317
+ rb_define_method(rb_cDBCFile, "initialize", dbc_initialize, 2);
318
+ rb_define_method(rb_cDBCFile, "read", dbc_read, 0);
319
+ rb_define_method(rb_cDBCFile, "write", dbc_write, 0);
320
+ rb_define_method(rb_cDBCFile, "create_record", dbc_create_record, 0);
321
+ rb_define_method(rb_cDBCFile, "create_record_with_values", dbc_create_record_with_values, 1);
322
+ rb_define_method(rb_cDBCFile, "update_record", dbc_update_record, 3);
323
+ rb_define_method(rb_cDBCFile, "update_record_multi", dbc_update_record_multi, 2);
324
+ rb_define_method(rb_cDBCFile, "delete_record", dbc_delete_record, 1);
325
+ rb_define_method(rb_cDBCFile, "get_record", dbc_get_record, 1);
326
+ rb_define_method(rb_cDBCFile, "header", dbc_get_header, 0);
327
+ }
data/lefthook.yml ADDED
@@ -0,0 +1,7 @@
1
+ pre-commit:
2
+ commands:
3
+ rubocop:
4
+ run: bundle exec rubocop -A
5
+ skip:
6
+ - merge
7
+ - rebase
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WowDBC
4
+ VERSION = '1.0.0'
5
+ end
data/lib/wow_dbc.rb ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # File: lib/wow_dbc.rb
4
+
5
+ require 'wow_dbc/wow_dbc'
6
+ require 'wow_dbc/version'
7
+
8
+ module WowDBC
9
+ class DBCFile
10
+ # All functionality is implemented in C
11
+ end
12
+ end
data/sig/wow_dbc.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module WowDbc
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wow_dbc
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - sebi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-09-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: WowDBC provides a Ruby interface to read, write, and manipulate World
14
+ of Warcraft DBC (Database Client) files. It offers efficient CRUD operations and
15
+ seamless integration with Ruby projects, making it ideal for WoW addon development,
16
+ data analysis, and game modding.
17
+ email:
18
+ - gore.sebyx@yahoo.com
19
+ executables: []
20
+ extensions:
21
+ - ext/wow_dbc/extconf.rb
22
+ extra_rdoc_files: []
23
+ files:
24
+ - ".rspec"
25
+ - ".rubocop.yml"
26
+ - CHANGELOG.md
27
+ - CODE_OF_CONDUCT.md
28
+ - LICENSE.txt
29
+ - README.md
30
+ - Rakefile
31
+ - ext/wow_dbc/extconf.rb
32
+ - ext/wow_dbc/wow_dbc.c
33
+ - lefthook.yml
34
+ - lib/wow_dbc.rb
35
+ - lib/wow_dbc/version.rb
36
+ - sig/wow_dbc.rbs
37
+ homepage: https://github.com/sebyx07/wow_dbc
38
+ licenses:
39
+ - MIT
40
+ metadata:
41
+ allowed_push_host: https://rubygems.org
42
+ homepage_uri: https://github.com/sebyx07/wow_dbc
43
+ source_code_uri: https://github.com/sebyx07/wow_dbc
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.0.0
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubygems_version: 3.5.17
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: A high-performance Ruby gem for reading and manipulating World of Warcraft
63
+ DBC files
64
+ test_files: []