Day.png);">
Apprendre


Vous êtes
nouveau sur
Oniromancie?

Visite guidée
du site


Découvrir
RPG Maker


Apprendre
RPG Maker

Tutoriels
Guides
Making-of

Dans le
Forum

Section Entraide

Jeux: puie z / Jeux: Citymaime - Chapitre 1 / Jeux: Mer, îles et fous / News: Du neuf dans le making / News: Muma|Rope est disponible en (...) / Chat

Bienvenue
visiteur !




publicité RPG Maker!

Statistiques

Liste des
membres


Contact

Mentions légales

688 connectés actuellement

30729219 visiteurs
depuis l'ouverture

2836 visiteurs
aujourd'hui



Barre de séparation

Partenaires

Indiexpo

Akademiya RPG Maker

Blog Alioune Fall

Fairy Tail Constellations

Offgame

ConsoleFun

Leo-Games

RPG Maker - La Communauté

Tous nos partenaires

Devenir
partenaire



Module C 5.1

[Outil d'aide au scripteur] Permet de créer/lire/altérer des données typée C de manière simple et efficace afin de faciliter l'utilisation avancée de dll en Ruby.

Script pour RPG Maker XP
Ecrit par Zeus81
Publié par zeus81 (lui envoyer un message privé)
Signaler un script cassé

❤ 0

Auteur : Zeus81
Logiciel : RPG Maker XP
Nombre de scripts : 1

Attention, ce script est réservé aux scripteurs ! Si vous ne comprenez pas son utilité ou ne savez pas scripter, il vous sera totalement inutile.

Explication
A la base j'ai fait ça pour moi et mes prochains scripts ultimes mais comme je vois qu'il y en a certains dans le coin qui font des scripts utilisant des dll en C je le partage avant l'heure.
Ce script permet de créer/lire/altérer des données typées C en Ruby.
Les avantages sont que c'est plus simple et efficace à utiliser que des String avec pack/unpack, les données pouvant être lues ET modifiées aussi bien en C qu'en Ruby et puis tout est automatique.
Et tout cela avec ce script de seulement 203 lignes, vous me direz sûrement "Jamais 203" et je vous répondrai "Oh que si !".

Installation
A placer au-dessus de Main.

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
module C # v 5.1 by Zeus81
  RtlMoveMemory = Win32API.new('kernel32', 'RtlMoveMemory', 'iii', '')
  def self.memcpy(destination, source, size)
    RtlMoveMemory.call(
        destination.is_a?(String) ? string_address(destination) : destination,
        source.is_a?(String) ? string_address(source) : source, size)
    destination
  rescue raise($!, $!.message, caller)
  end
  
  def self.string_address(string) [string].pack('p').unpack('L')[0]
  rescue raise($!, $!.message, caller)
  end
  
  @anonymous_class_count = 0
  def self.name_anonymous_class(type)
    if type.name.empty?
      type.superclass.name =~ /.*::(.*)/
      const_set("Anonymous#{$1}_%02X" % @anonymous_class_count+=1, type)
    end
  rescue raise($!, $!.message, caller)
  end
  
  def self.make_accessors(klass, data, child=nil)
    data.each_with_index do |d,i|
      if d[2] != nil
        a = d[2] == :[] ? 'i,' : ''
        b = d[2] == :[] ? '[i]' : child ? ".#{d[2]}" : "[#{i}]"
        b = "[#{child}]#{b}" if child
        c = b + (child ? '=' : '.set')
        b << '.get' unless child or d[0] < CData
        klass << "
        define_method(:#{d[2]}) {|#{a[0,1]}| @children#{b}}
        define_method(:#{d[2]}=) {|#{a}v| @children#{c}(v)}"
        break if d[2] == :[]
      else make_accessors(klass, d[0].data, child || i)
      end
    end
  rescue raise($!, $!.message, caller)
  end
  
  class CType
    def self.format() self::FORMAT end
    def format()       type.format end
    def self.size()   self::SIZE   end
    def size()         type.size   end
    def initialize(*v, &b)
      @address = b ? b.call.to_int : C.string_address(@string=("\0"*size).freeze)
      @children = data.map {|t,o| t.new {to_int+o}} if type < C::CData
      set(*v) unless v.empty?
    rescue raise($!, $!.message, caller)
    end
    def get()  unpack
    rescue raise($!, $!.message, caller)
    end
    def set(v) pack(v)
    rescue raise($!, $!.message, caller)
    end
    def unpack() to_str.unpack(format)[0].freeze
    rescue raise($!, $!.message, caller)
    end
    def pack(v)  C.memcpy(self, v.is_a?(type) ? v : [v].pack(format), size)
    rescue raise($!, $!.message, caller)
    end
    def to_int() @address
    rescue raise($!, $!.message, caller)
    end
    def to_str() @string or C.memcpy(("\0"*size).freeze, self, size)
    rescue raise($!, $!.message, caller)
    end
  end
  
  class CData < CType
    def self.data() self::DATA end
    def data()       type.data end
    def get() @children.map {|c| c.get}.freeze
    rescue raise($!, $!.message, caller)
    end
    def set(*v)
      if    v[0].is_a?(type); super(v[0])
      elsif v[0].is_a?(Hash)
        v[0].each {|n,v| n.is_a?(Integer) ? @children[n].set(*v) : send("#{n}=",v)}
      else v.each_with_index {|v,i| @children[i].set(*v)}
      end
    rescue raise($!, $!.message, caller)
    end
    def [](i)    data[i][0] < CData ? @children[i] : @children[i].get
    rescue raise($!, $!.message, caller)
    end
    def []=(i,v) @children[i].set(*v)
    rescue raise($!, $!.message, caller)
    end
  end
  
  CStruct, CUnion, CArray = Class.new(CData), Class.new(CData), Class.new(CData)
  
  def self.Class(type, klass)
    raise(TypeError,"Type expected, got #{type.type}") unless type.is_a?(Class) and
                                                              type <= CType
    Class.new(type) {class_eval(klass, __FILE__, __LINE__)}
  rescue raise($!, $!.message, caller)
  end
  
  def self.Type(format, size)
    raise(TypeError,"String expected, got #{format.type}") unless format.is_a?(String)
    raise(TypeError,"Integer expected, got #{size.type}") unless size.is_a?(Integer)
    Class(CType, "SIZE, FORMAT = #{size}, '#{format}'.freeze")
  rescue raise($!, $!.message, caller)
  end
  
  def self.Enum(type, *variables)
    h, v = {}, -1
    variables.each_with_index do |n,i|
      next unless n.is_a?(Symbol)
      h[n] = v = (next_v=variables[i+1]).is_a?(Symbol) ? v.succ : next_v
    end
    Class(type, "DATA = #{h.inspect}.freeze
    def self.method_missing(sym, *args) DATA[sym] or super
    rescue raise($!, $!.message, caller)
    end")
  rescue raise($!, $!.message, caller)
  end
  
  def self.Data(type, variables, array_size=nil)
    size, data, t = 0, [], nil
    variables.each_with_index do |n,i|
      if n.is_a?(Class) and n < CType
        t, n = n, nil
        next if variables[i+1].is_a?(Symbol)
      end
      next unless t != nil and (n==nil or n.is_a?(Symbol))
      name_anonymous_class(t)
      if    type == CStruct
        data << [t,size,n]
        size = size+t.size
      elsif type == CUnion
        data << [t,0,n]
        size = t.size if t.size > size
      elsif type == CArray
        data.replace(Array.new(array_size) {|j| [t,j*t.size,n]})
        size = t.size*array_size
      end
    end
    klass = "SIZE, FORMAT, DATA = #{size}, 'a#{size}'.freeze, #{data.inspect}.freeze"
    make_accessors(klass, data) unless type == CArray
    Class(type, klass)
  rescue raise($!, $!.message, caller)
  end
  
  def self.Struct(*variables) Data(CStruct, variables)
  rescue raise($!, $!.message, caller)
  end
  
  def self.Union (*variables) Data(CUnion , variables)
  rescue raise($!, $!.message, caller)
  end
  
  def self.Array (type, *dimensions)
    dimensions.reverse_each {|s| type=Data(CArray , [type,:[]], s)}
    type
  rescue raise($!, $!.message, caller)
  end
  
  CHAR      =             Type('c',1)
  UCHAR     = BYTE      = Type('C',1)
  SHORT     =             Type('s',2)
  USHORT    = WORD      = Type('S',2)
  INT       =             Type('i',4)
  UINT      =             Type('I',4)
  LONG      =             Type('l',4)
  ULONG     = DWORD     = Type('L',4)
  LONGLONG  =             Type('q',8)
  ULONGLONG = DWORDLONG = Type('Q',8)
  FLOAT     =             Type('f',4)
  DOUBLE    =             Type('d',8)
  BOOLEAN   =             Type('C',1)
  BOOL      =             Type('i',4)
  POINTER   =             Type('L',4)
  class BOOLEAN
    def unpack() super==0 ? false : true
    rescue raise($!, $!.message, caller)
    end
    def pack(v)  super(!v || v==0 ? 0 : 1)
    rescue raise($!, $!.message, caller)
    end
  end
  class BOOL
    def unpack() super==0 ? false : true
    rescue raise($!, $!.message, caller)
    end
    def pack(v)  super(!v || v==0 ? 0 : 1)
    rescue raise($!, $!.message, caller)
    end
  end
  class POINTER
    def unpack() @pointer
    rescue raise($!, $!.message, caller)
    end
    def pack(v)  super(@pointer=v ? v.to_int : 0)
    rescue raise($!, $!.message, caller)
    end
  end
end





Type :
On a 15 types de données prédéfinis et il n'y en a pas besoin de plus.
CHAR = C signed char
UCHAR = BYTE = C unsigned char
SHORT = C signed short
USHORT = WORD = C unsigned short
INT = C signed int
UINT = C unsigned int
LONG = C signed long
ULONG = DWORD = C unsigned long
LONGLONG = C signed long long
ULONGLONG = DWORDLONG = C unsigned long long
FLOAT = C float
DOUBLE = C double
BOOLEAN qui en fait est un UCHAR mais qui gère true et false de Ruby.
BOOL qui en fait est un INT mais qui gère true et false de Ruby.
POINTER qui peut pointer sur n'importe quel objet fourni par le module C.
N'importe quel autre type peut être converti en un de ceux là (vous pouvez vous aider de ça).



Enum :
C.Enum(type, [nom, [valeur, [nom, [valeur ...]]]])
C'est pas indispensable, c'est juste pour faciliter la création de constantes.
type est le Type qu'aura l'Enum au cas où on l'utiliserait dans un Struct (généralement c'est INT).
Les noms doivent être passés sous forme de symbole.
Les valeurs doivent être des entiers.
Si une valeur est omise la variable prendra la valeur précédente+1, ou 0 si c'est la première.

Portion de code : Tout sélectionner

1
2
3
Couleur = C.Enum(C::INT, :rouge, 0,
                         :vert , 1,
                         :bleu , 2)

équivaut à :

Portion de code : Tout sélectionner

1
2
3
Couleur = C.Enum(C::INT, :rouge,
                         :vert,
                         :bleu)


et pour lire une valeur on fait :

Portion de code : Tout sélectionner

1
Couleur.vert





Array :
C.Array(type, [dimension1, [dimension2 ...]])
type c'est n'importe quel Type du module C pour tous les éléments du tableau.
On peut spécifier autant de dimensions qu'on veut avec des entiers.

Portion de code : Tout sélectionner

1
2
I4 = C.Array(C::INT, 4)
tab = I4.new


I4 est la classe qui me permet de créer des tableaux de 4 entiers.
En général pour les tableaux on utilisera des classes anonymes en faisant directement :

Portion de code : Tout sélectionner

1
tab = C.Array(C::INT, 4).new


Par défaut un tableau est initialisé avec tout à 0, il existe plusieurs moyens de changer les valeurs.
Directement lors de l'initialisation on peut passer des paramètres dans l'ordre :

Portion de code : Tout sélectionner

1
tab = I4.new(60, 61, 62, ...)


dans le désordre :

Portion de code : Tout sélectionner

1
tab = I4.new(2=>62, 0=>60, ...)


ou avec un autre tableau de la même classe :

Portion de code : Tout sélectionner

1
tab2 = I4.new(tab1)


Une fois l'objet créé on peut toujours modifier ses données avec la fonction set (qui fonctionne comme new) ou avec l'opérateur []=

Portion de code : Tout sélectionner

1
tab[0] = 60


Pour lire les données on utilise []

Portion de code : Tout sélectionner

1
tab[0] # => 60


ou la méthode get qui retournera un tableau Ruby de toutes les valeurs

Portion de code : Tout sélectionner

1
tab.get # => [60, 61, 62, 63]


Les tableaux multidimensionnels ne sont en fait que des tableaux de tableaux :

Portion de code : Tout sélectionner

1
C.Array(C::INT, 4, 3)

équivaut à :

Portion de code : Tout sélectionner

1
C.Array(C.Array(C::INT, 3), 4)


Et donc on peut modifier les données de plusieurs façons différentes :

Portion de code : Tout sélectionner

1
2
3
4
5
6
tab = C.Array(C::INT, 4, 3).new
tab[0][2] = 1
tab[1].set(2, 3, 4)
tab[2] = [5, 6, 7]
tab.set(3=>[8, 9])
tab.get # => [[0, 0, 1], [2, 3, 4], [5, 6, 7], [8, 9, 0]]


Je sais, j'explique très mal, mais normalement ça devrait être assez logique. image



Struct :
C.Struct([type, [nom, [type, [nom, ...]]]])
type peut être un INT, CHAR, etc... mais aussi Struct, Union, Enum, Array.
Les noms doivent être passés sous forme de symbole.
Si un type est omis la variable prendra le type précédent.
Si un nom est omis la variable sera anonyme.
Si la variable anonyme est un Struct, Union ou Array, ses fonctions seront hérités (comme en C).

En gros un struct c'est comme un Array sauf que chaque élément a un type et un nom différent.

Portion de code : Tout sélectionner

1
2
Fruit = C.Struct(C::FLOAT, :diametre,
                 C::BOOL , :pepins)


Comme pour un Array il y a plusieurs manières de configurer un Struct :

Portion de code : Tout sélectionner

1
2
3
4
5
6
pomme = Fruit.new
pomme.set(5.8, true)
pomme.set(:diametre=>5.8, :pepins=>true)
pomme.set(0=>5.8, 1=>true)
pomme[0] = 5.8 # Attention si le Struct contient un Array anonyme, cela n'est plus possible
pomme.pepins = true


Même avec un Struct get renvoie toujours un tableau des données :

Portion de code : Tout sélectionner

1
pomme.get # => [5.8, true]


Attention, si un Struct contient un autre Struct il faudra grouper les données comme pour les tableaux multidimensionnels.

Portion de code : Tout sélectionner

1
2
3
4
5
6
Arbre = C.Struct(C::INT , :taille,
                 Fruit  , :fruit,
                 Couleur, :couleur)
Arbre.new(9, 5.8, true, Couleur.vert) # => erreur
Arbre.new(9, [5.8, true], Couleur.vert) # => ok
Arbre.new(9, pomme, Couleur.vert) # => ok





Union :
Marche comme Struct à quelques détails près.
Exemple d'un Union avec Struct et Array anonymes :

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MATRIX = C.Union(C.Struct(FLOAT, :_11, :_12, :_13, :_14,
                                 :_21, :_22, :_23, :_24,
                                 :_31, :_32, :_33, :_34,
                                 :_41, :_42, :_43, :_44),
                 C.Array(FLOAT,4,4))
m = MATRIX.new(0=>[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])
# Attention, il faut choisir selon quel élément de l'Union on va modifier les données,
# vu que les deux sont anonymes je fais avec les id, ici 0 c'est le Struct, pour l'Array j'aurais dû faire :
# m = MATRIX.new(1=>[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
m._43 == m[3][2] # => true
                 # Vu que les éléments sont anonymes leurs fonctions sont hérités.
                 # m[3] fait non pas appel à l'élément 3 de m mais à la ligne 3 de l'Array.
m.get # => [ [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
      #      [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] ]
      # le get d'un Union retourne un tableau contenant tous ses formats.





Pointer :
Un petit topo sur les pointeurs vu que c'est pas forcément évident.

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
A = C.Struct(C::POINTER, :ptr)
a = A.new
b = C::CHAR.new(7) # char*
a.ptr = b
a.ptr.get # => 7
b.set(45)
a.ptr.get # => 45
a.ptr.set(-16)
b.get # => -16

Je pense que c'est suffisamment explicite.

Autre exemple sur ce qui correspondrait à une double indirection :

Portion de code : Tout sélectionner

1
2
a.ptr = C::POINTER.new(C::INT.new(128)) # int**
a.ptr.get.get # => 128


Et comme dit plus haut, un pointeur peut pointer sur n'importe quel objet du module C :

Portion de code : Tout sélectionner

1
2
a.ptr = a
a.ptr.get # => a


Autre chose sur les pointeurs, si on récupère un pointeur via une Api :

Portion de code : Tout sélectionner

1
ptr = Win32API.new('dll', 'GetPointer', '', 'i').call


On peut récupérer/modifier la valeur vers où il pointe quel que soit le type, par exemple si c'est un float :

Portion de code : Tout sélectionner

1
2
3
f = C::FLOAT.new {ptr}
f.get # => récupère la valeur en mémoire
f.set(0.1) # la modifie


Et ça marche aussi avec les Struct/Array/Union :

Portion de code : Tout sélectionner

1
2
MonStruct = C.Struct(...)
s = MonStruct.new {ptr}


Bien sûr il faut que MonStruct soit l'exacte réplique du struct C vers qui il pointe.
Attention cependant aux allocations de mémoire, si je récupère le pointeur vers une donnée de la dll qui a été libérée il risque d'y avoir des problèmes.



Exemple simple et concret d'utilisation, si je veux reproduire cette structure et utiliser cette Api je fais :

Portion de code : Tout sélectionner

1
2
3
4
5
POINT = C.Struct(C::LONG, :x, :y)
GetCursorPos = Win32API.new('user32', 'GetCursorPos', 'i', 'i') # on met 'i' pour le struct et non 'p' pour éviter les copies dans certains cas
$cursor = POINT.new
GetCursorPos.call($cursor) # on passe directement l'objet
$cursor.x # retourne la position x du curseur


Bon bien sûr ça vaut pas vraiment le coût dans cet exemple d'utiliser mon script de 203 lignes (si si) alors qu'avec un string il nous en faudrait 4 mais c'est pour ceux qui veulent faire des trucs hautement plus compliqués.

J'avais un peu la flemme de tout expliquer en détail alors si vous avez des questions (sur le module uniquement) n'hésitez pas à les poser les ici.


Mis à jour le 18 novembre 2020.






Tata Monos - posté le 15/04/2011 à 18:22:02 (28 messages postés)

❤ 0

Compte Non utilisé

On va peut être y arriver un jour à régler ce bug xd


Sylvanor - posté le 15/04/2011 à 18:30:06 (24804 messages postés) - webmaster

❤ 0

Le gars chiant qui rigole jamais (il paraît)

GG!
Presque aussi bien qu'un tuto de Zeus81!

Les croissants croâssent en croix, s'ancrent ou à cent croîssent sans crocs à sang. Crois! Sens! ౡ


Tata Monos - posté le 15/04/2011 à 18:35:50 (28 messages postés)

❤ 0

Compte Non utilisé

Merci. xd

Edit : la il va me maudire nonor xd


charles ingalls - posté le 15/04/2011 à 18:45:59 (999 messages postés)

❤ 0

Citation:

avec ce script de seulement 203 lignes, vous me direz sûrement "Jamais 203" et je vous répondrait "Oh que si !


:lol elle est pas mal celle là


Tata Monos - posté le 15/04/2011 à 19:05:38 (28 messages postés)

❤ 0

Compte Non utilisé

yeah c'est corrigé aller nouga.


Zeus[ ] - posté le 15/04/2011 à 19:44:29 (11071 messages postés)

❤ 0

Amen. :lei
Bon maintenant faudrait penser à supprimer l'ancien. :F


Tata Monos - posté le 15/04/2011 à 20:10:45 (28 messages postés)

❤ 0

Compte Non utilisé

:D
Suite au prochain épisode.

Suite à de nombreux abus, le post en invités a été désactivé. Veuillez vous inscrire si vous souhaitez participer à la conversation.

Haut de page

Merci de ne pas reproduire le contenu de ce site sans autorisation.
Contacter l'équipe - Mentions légales

Plan du site

Communauté: Accueil | Forum | Chat | Commentaires | News | Flash-news | Screen de la semaine | Sorties | Tests | Gaming-Live | Interviews | Galerie | OST | Blogs | Recherche
Apprendre: Visite guidée | RPG Maker 95 | RPG Maker 2003 | RPG Maker XP | RPG Maker VX | RPG Maker MV | Tutoriels | Guides | Making-of
Télécharger: Programmes | Scripts/Plugins | Ressources graphiques / sonores | Packs de ressources | Midis | Eléments séparés | Sprites
Jeux: Au hasard | Notre sélection | Sélection des membres | Tous les jeux | Jeux complets | Le cimetière | RPG Maker 95 | RPG Maker 2000 | RPG Maker 2003 | RPG Maker XP | RPG Maker VX | RPG Maker VX Ace | RPG Maker MV | Autres | Proposer
Ressources RPG Maker 2000/2003: Chipsets | Charsets | Panoramas | Backdrops | Facesets | Battle anims | Battle charsets | Monstres | Systems | Templates
Ressources RPG Maker XP: Tilesets | Autotiles | Characters | Battlers | Window skins | Icônes | Transitions | Fogs | Templates
Ressources RPG Maker VX: Tilesets | Charsets | Facesets | Systèmes
Ressources RPG Maker MV: Tilesets | Characters | Faces | Systèmes | Title | Battlebacks | Animations | SV/Ennemis
Archives: Palmarès | L'Annuaire | Livre d'or | Le Wiki | Divers