OpenClonk
group.c
Go to the documentation of this file.
1 /*
2  * mape - C4 Landscape.txt editor
3  *
4  * Copyright (c) 2005-2009, Armin Burgmeier
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
28 #include <string.h> /* for strrchr */
29 
31 #include "mape/group.h"
32 
33 #ifdef G_OS_WIN32
34 #include <windows.h>
35 #endif
36 
37 /* Declare private API */
39 _mape_group_get_handle(MapeGroup* group); /* shut up gcc */
40 
41 typedef struct _MapeGroupPrivate MapeGroupPrivate;
44 
45  /* On Windows, the root directory "/" is interpreted as a directory
46  * containing the local drives (C:\, D:\, etc.). handle is set to NULL for
47  * a group representing the "/" directory. */
48 #ifdef G_OS_WIN32
49  /* This is 0 if the group is closed, otherwise the currently accessed drive
50  * index (as returned by GetLogicalDrives()) minus one. */
51  guint drive;
52 #endif
53 };
54 
55 enum {
57 
58  /* read only */
61 };
62 
63 #define MAPE_GROUP_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), MAPE_TYPE_GROUP, MapeGroupPrivate))
64 
65 static GQuark mape_group_error_quark;
66 
67 G_DEFINE_TYPE(MapeGroup, mape_group, G_TYPE_OBJECT)
68 
69 /*
70  * GObject overrides.
71  */
72 
73 static void
74 mape_group_init(MapeGroup* group)
75 {
76  MapeGroupPrivate* priv;
77  priv = MAPE_GROUP_PRIVATE(group);
78 
79  priv->handle = NULL;
80 
81 #ifdef G_OS_WIN32
82  priv->drive = 0;
83 #endif
84 }
85 
86 static void
87 mape_group_finalize(GObject* object)
88 {
89  MapeGroup* group;
90  MapeGroupPrivate* priv;
91 
92  group = MAPE_GROUP(object);
93  priv = MAPE_GROUP_PRIVATE(group);
94 
95  if(priv->handle != NULL)
97 
98  G_OBJECT_CLASS(mape_group_parent_class)->finalize(object);
99 }
100 
101 static void
102 mape_group_set_property(GObject* object,
103  guint prop_id,
104  const GValue* value,
105  GParamSpec* pspec)
106 {
107  MapeGroup* group;
108  MapeGroupPrivate* priv;
109 
110  group = MAPE_GROUP(object);
111  priv = MAPE_GROUP_PRIVATE(group);
112 
113  switch(prop_id)
114  {
115  /* we have only readonly properties */
116  default:
117  G_OBJECT_WARN_INVALID_PROPERTY_ID(value, prop_id, pspec);
118  break;
119  }
120 }
121 
122 static void
123 mape_group_get_property(GObject* object,
124  guint prop_id,
125  GValue* value,
126  GParamSpec* pspec)
127 {
128  MapeGroup* group;
129  MapeGroupPrivate* priv;
130 
131  group = MAPE_GROUP(object);
132  priv = MAPE_GROUP_PRIVATE(group);
133 
134  switch(prop_id)
135  {
136  case PROP_NAME:
137  g_value_set_string(value, mape_group_get_name(group));
138  break;
139  case PROP_FULL_NAME:
140  g_value_take_string(value, mape_group_get_full_name(group));
141  break;
142  default:
143  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
144  break;
145  }
146 }
147 
148 /*
149  * Gype registration.
150  */
151 
152 static void
153 mape_group_class_init(MapeGroupClass *class)
154 {
155  GObjectClass* object_class;
156 
157  object_class = G_OBJECT_CLASS(class);
158  mape_group_parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(class));
159  g_type_class_add_private(class, sizeof(MapeGroupPrivate));
160 
161  object_class->finalize = mape_group_finalize;
162  object_class->set_property = mape_group_set_property;
163  object_class->get_property = mape_group_get_property;
164 
165  mape_group_error_quark = g_quark_from_static_string("MAPE_GROUP_ERROR");
166 
167  g_object_class_install_property(
168  object_class,
169  PROP_NAME,
170  g_param_spec_string(
171  "name",
172  "Name",
173  "The name of the group",
174  NULL,
175  G_PARAM_READABLE
176  )
177  );
178 
179  g_object_class_install_property(
180  object_class,
182  g_param_spec_string(
183  "full-name",
184  "Full Name",
185  "The full path to the group",
186  NULL,
187  G_PARAM_READABLE
188  )
189  );
190 }
191 
192 /*
193  * Public API.
194  */
195 
205 MapeGroup*
207 {
208  return MAPE_GROUP(g_object_new(MAPE_TYPE_GROUP, NULL));
209 }
210 
219 gboolean
221 {
222  MapeGroupPrivate* priv;
223 
224  g_return_val_if_fail(MAPE_IS_GROUP(group), FALSE);
225 
226  priv = MAPE_GROUP_PRIVATE(group);
227 
228  if(priv->handle != NULL) return TRUE;
229 #ifdef G_OS_WIN32
230  if(priv->drive != 0) return TRUE;
231 #endif
232  return FALSE;
233 }
234 
248 gboolean
250  const gchar* path,
251  GError** error)
252 {
253  MapeGroupPrivate* priv;
254 
255  g_return_val_if_fail(MAPE_IS_GROUP(group), FALSE);
256  g_return_val_if_fail(path != NULL, FALSE);
257  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
258  g_return_val_if_fail(mape_group_is_open(group) == FALSE, FALSE);
259 
260  priv = MAPE_GROUP_PRIVATE(group);
261 
262 #ifdef G_OS_WIN32
263  if(strcmp(path, "/") == 0)
264  {
265  priv->drive = 1;
266  return TRUE;
267  }
268 #endif
269 
270  priv->handle = c4_group_handle_new();
271  if(c4_group_handle_open(priv->handle, path, FALSE) == FALSE)
272  {
273  g_set_error(error, mape_group_error_quark, MAPE_GROUP_ERROR_OPEN,
274  "Could not open '%s': %s", path,
277  priv->handle = NULL;
278  return FALSE;
279  }
280 
281  g_object_notify(G_OBJECT(group), "name");
282  g_object_notify(G_OBJECT(group), "full-name");
283 
284  return TRUE;
285 }
286 
300 MapeGroup*
302  const gchar* entry,
303  GError** error)
304 {
305  MapeGroupPrivate* parent_priv;
306  MapeGroup* child;
307  MapeGroupPrivate* child_priv;
308  C4GroupHandle* child_handle;
309 
310  g_return_val_if_fail(MAPE_IS_GROUP(group), NULL);
311  g_return_val_if_fail(entry != NULL, NULL);
312  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
313  g_return_val_if_fail(mape_group_is_open(group) == TRUE, NULL);
314 
315  parent_priv = MAPE_GROUP_PRIVATE(group);
316 #ifdef G_OS_WIN32
317  if(parent_priv->handle == NULL)
318  {
319  child = mape_group_new();
320  if(!mape_group_open(child, entry, error))
321  {
322  g_object_unref(child);
323  return NULL;
324  }
325 
326  return child;
327  }
328 #endif
329 
330  child_handle = c4_group_handle_new();
331  if(!c4_group_handle_open_as_child(child_handle, parent_priv->handle,
332  entry, FALSE, FALSE))
333  {
334  g_set_error(error, mape_group_error_quark, MAPE_GROUP_ERROR_OPEN,
335  "Could not open '%s': %s", entry,
336  c4_group_handle_get_error(child_handle));
337  c4_group_handle_free(child_handle);
338  return NULL;
339  }
340 
341  child = mape_group_new();
342  child_priv = MAPE_GROUP_PRIVATE(child);
343  child_priv->handle = child_handle;
344 
345  return child;
346 }
347 
354 void
356 {
357  MapeGroupPrivate* priv;
358 
359  g_return_if_fail(MAPE_IS_GROUP(group));
360  g_return_if_fail(mape_group_is_open(group) == FALSE);
361 
362  priv = MAPE_GROUP_PRIVATE(group);
363 
364 #ifdef G_OS_WIN32
365  priv->drive = 0;
366 #endif
367 
368  if(priv->handle != NULL)
369  {
371  priv->handle = NULL;
372 
373  /* Don't notify when only drive was reset, as name and full-name are
374  * not specified for these anyway (yet). */
375  g_object_notify(G_OBJECT(group), "name");
376  g_object_notify(G_OBJECT(group), "full-name");
377  }
378 }
379 
389 const gchar*
391 {
392  g_return_val_if_fail(MAPE_IS_GROUP(group), NULL);
393  g_return_val_if_fail(mape_group_is_open(group), NULL);
394 
395  return c4_group_handle_get_name(MAPE_GROUP_PRIVATE(group)->handle);
396 }
397 
406 gchar*
408 {
409  g_return_val_if_fail(MAPE_IS_GROUP(group), NULL);
410  g_return_val_if_fail(mape_group_is_open(group), NULL);
411 
412  return c4_group_handle_get_full_name(MAPE_GROUP_PRIVATE(group)->handle);
413 }
414 
423 gboolean
425  const gchar* entry)
426 {
427  MapeGroupPrivate* priv;
428 #ifdef G_OS_WIN32
429  DWORD chk_drv;
430 #endif
431 
432  g_return_val_if_fail(MAPE_IS_GROUP(group), FALSE);
433  g_return_val_if_fail(mape_group_is_open(group), FALSE);
434  g_return_val_if_fail(entry != NULL, FALSE);
435 
436  priv = MAPE_GROUP_PRIVATE(group);
437 
438 #ifdef G_OS_WIN32
439  if(priv->handle == NULL)
440  {
441  if(entry[0] == '\0') return FALSE;
442  if(entry[1] != ':') return FALSE;
443 
444  chk_drv = 1 << (entry[0] - 'A');
445  return (GetLogicalDrives() & chk_drv) != 0;
446  }
447 #endif
448 
450  return c4_group_handle_find_next_entry(priv->handle, entry,
451  NULL, NULL, FALSE);
452 }
453 
461 void
463 {
464  MapeGroupPrivate* priv;
465 
466  g_return_if_fail(MAPE_IS_GROUP(group));
467  g_return_if_fail(mape_group_is_open(group));
468 
469  priv = MAPE_GROUP_PRIVATE(group);
470 
471 #ifdef G_OS_WIN32
472  if(priv->handle == NULL)
473  {
474  priv->drive = 1;
475  return;
476  }
477 #endif
478 
480 }
481 
495 gchar*
497 {
498  MapeGroupPrivate* priv;
499  gchar* buf;
500 #ifdef G_OS_WIN32
501  DWORD drv_c;
502  guint drive;
503 #endif
504 
505  g_return_val_if_fail(MAPE_IS_GROUP(group), NULL);
506  g_return_val_if_fail(mape_group_is_open(group), NULL);
507 
508  priv = MAPE_GROUP_PRIVATE(group);
509 
510 #ifdef G_OS_WIN32
511  static const guint DRV_C_SUPPORT = 26;
512  if(priv->handle == NULL)
513  {
514  drv_c = GetLogicalDrives();
515 
516  /* Find next available drive or wait for overflow */
517  drive = priv->drive - 1;
518  while( (drive < DRV_C_SUPPORT) && ((~drv_c & (1 << drive)) != 0))
519  ++drive;
520  if(drive >= DRV_C_SUPPORT) return NULL;
521 
522  buf = g_malloc(3 * sizeof(gchar));
523  buf[0] = 'A' + drive;
524  buf[1] = ':';
525  buf[2] = '\0';
526  priv->drive = drive + 2;
527 
528  return buf;
529  }
530 #endif
531 
532  buf = g_malloc(512 * sizeof(gchar));
533  if(!c4_group_handle_find_next_entry(priv->handle, "*", NULL, buf, FALSE))
534  {
535  g_free(buf);
536  buf = NULL;
537  }
538 
539  return buf;
540 }
541 
556 guchar*
558  const gchar* entry,
559  gsize* size,
560  GError** error)
561 {
562  MapeGroupPrivate* priv;
563  gsize s;
564  guchar* res;
565 
566  g_return_val_if_fail(MAPE_IS_GROUP(group), NULL);
567  g_return_val_if_fail(mape_group_is_open(group), NULL);
568  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
569 
570  priv = MAPE_GROUP_PRIVATE(group);
571  if(!c4_group_handle_access_entry(priv->handle, entry, &s, NULL, FALSE))
572  {
573  g_set_error(error, mape_group_error_quark, MAPE_GROUP_ERROR_ACCESS,
574  "%s", c4_group_handle_get_error(priv->handle));
575  return NULL;
576  }
577 
578  res = g_malloc(s);
579  if(!c4_group_handle_read(priv->handle, (char*)res, s))
580  {
581  g_set_error(error, mape_group_error_quark, MAPE_GROUP_ERROR_READ,
582  "%s", c4_group_handle_get_error(priv->handle));
583  g_free(res);
584  return NULL;
585  }
586 
587  if(size != NULL) *size = s;
588  return res;
589 }
590 
591 /*
592  * mape_group_is_drive_container:
593  * @group: An open #MapeGroup.
594  *
595  * Returns whether @group is a proxy folder which contains the available
596  * drives on Windows (C:\, D:\, etc.) as subfolders. This is what one gets
597  * when opening the root folder "/" on Windows. On Unix, the function always
598  * returns %FALSE.
599  *
600  * Returns: Whether @group contains the available drives.
601  */
602 gboolean
604 {
605  g_return_val_if_fail(MAPE_IS_GROUP(group), FALSE);
606  g_return_val_if_fail(mape_group_is_open(group), FALSE);
607 
608 #ifdef G_OS_WIN32
609  {
610  MapeGroupPrivate* priv = MAPE_GROUP_PRIVATE(group);
611  if(priv->handle == NULL)
612  return TRUE;
613  }
614 #endif
615 
616  return FALSE;
617 }
618 
619 /*
620  * mape_group_is_child_folder:
621  * @group: An open #MapeGroup.
622  * @child: A child entry.
623  *
624  * Returns whether the given entry is a subgroup or not. The current
625  * implementation only works exactly for directories, not for packed groups.
626  * For the latter it makes only a guess upon the extension of the entry.
627  *
628  * Returns: %TRUE if @child is a subgroup of @group, %FALSE otherwise.
629  */
630 gboolean
632  const gchar* child)
633 {
634  MapeGroupPrivate* priv;
635  const gchar* ext;
636  gchar* fullname;
637  gchar* filename;
638  gboolean result;
639 
640  g_return_val_if_fail(MAPE_IS_GROUP(group), FALSE);
641  g_return_val_if_fail(mape_group_is_open(group), FALSE);
642  g_return_val_if_fail(child != NULL, FALSE);
643 
644  priv = MAPE_GROUP_PRIVATE(group);
645 
646 #ifdef G_OS_WIN32
647  /* Drives are always folders */
648  if(priv->handle == NULL)
649  return TRUE;
650 #endif
651 
652  /* Guess on extension */
653  ext = strrchr(child, '.');
654  if(ext != NULL)
655  {
656  if(g_ascii_strcasecmp(ext, ".ocs") == 0 ||
657  g_ascii_strcasecmp(ext, ".ocd") == 0 ||
658  g_ascii_strcasecmp(ext, ".ocf") == 0 ||
659  g_ascii_strcasecmp(ext, ".ocg") == 0)
660  {
661  return TRUE;
662  }
663  }
664 
665  /* Otherwise assume it's not a subgroup */
667  return FALSE;
668 
669  /* It is an open directory - check for regular directory */
670  fullname = c4_group_handle_get_full_name(priv->handle);
671  filename = g_build_filename(fullname, child, NULL);
672  g_free(fullname);
673 
674  result = g_file_test(filename, G_FILE_TEST_IS_DIR);
675  g_free(filename);
676 
677  return result;
678 }
679 
680 /* This function is for internal use only */
683 {
684  g_return_val_if_fail(MAPE_IS_GROUP(group), NULL);
685  return MAPE_GROUP_PRIVATE(group)->handle;
686 }
687 
688 /* vim:set et sw=2 ts=2: */
#define s
uint32_t DWORD
gboolean c4_group_handle_open_as_child(C4GroupHandle *handle, C4GroupHandle *mother, const gchar *name, gboolean exclusive, gboolean create)
void c4_group_handle_reset_search(C4GroupHandle *handle)
void c4_group_handle_free(C4GroupHandle *handle)
C4GroupHandle * c4_group_handle_new(void)
gboolean c4_group_handle_is_folder(C4GroupHandle *handle)
const gchar * c4_group_handle_get_name(C4GroupHandle *handle)
const gchar * c4_group_handle_get_error(C4GroupHandle *handle)
gboolean c4_group_handle_access_entry(C4GroupHandle *handle, const gchar *wildcard, gsize *size, gchar *filename, gboolean needs_to_be_a_group)
gboolean c4_group_handle_open(C4GroupHandle *handle, const gchar *path, gboolean create)
gboolean c4_group_handle_read(C4GroupHandle *handle, gpointer buffer, gsize size)
gboolean c4_group_handle_find_next_entry(C4GroupHandle *handle, const gchar *wildcard, gsize *size, gchar *filename, gboolean start_at_filename)
gchar * c4_group_handle_get_full_name(C4GroupHandle *handle)
typedefG_BEGIN_DECLS struct _C4GroupHandle C4GroupHandle
Definition: group-handle.h:23
@ PROP_NAME
Definition: group.c:59
@ PROP_FULL_NAME
Definition: group.c:60
@ PROP_0
Definition: group.c:56
guchar * mape_group_load_entry(MapeGroup *group, const gchar *entry, gsize *size, GError **error)
Definition: group.c:557
MapeGroup * mape_group_open_child(MapeGroup *group, const gchar *entry, GError **error)
Definition: group.c:301
gchar * mape_group_get_next_entry(MapeGroup *group)
Definition: group.c:496
void mape_group_close(MapeGroup *group)
Definition: group.c:355
MapeGroup * mape_group_new(void)
Definition: group.c:206
gboolean mape_group_is_drive_container(MapeGroup *group)
Definition: group.c:603
C4GroupHandle * handle
Definition: group.c:43
void mape_group_rewind(MapeGroup *group)
Definition: group.c:462
gboolean mape_group_is_child_folder(MapeGroup *group, const gchar *child)
Definition: group.c:631
gboolean mape_group_open(MapeGroup *group, const gchar *path, GError **error)
Definition: group.c:249
const gchar * mape_group_get_name(MapeGroup *group)
Definition: group.c:390
gchar * mape_group_get_full_name(MapeGroup *group)
Definition: group.c:407
#define MAPE_GROUP_PRIVATE(obj)
Definition: group.c:63
gboolean mape_group_has_entry(MapeGroup *group, const gchar *entry)
Definition: group.c:424
C4GroupHandle * _mape_group_get_handle(MapeGroup *group)
Definition: group.c:682
gboolean mape_group_is_open(MapeGroup *group)
Definition: group.c:220
#define MAPE_TYPE_GROUP
Definition: group.h:23
@ MAPE_GROUP_ERROR_OPEN
Definition: group.h:43
@ MAPE_GROUP_ERROR_ACCESS
Definition: group.h:44
@ MAPE_GROUP_ERROR_READ
Definition: group.h:45
#define MAPE_IS_GROUP(obj)
Definition: group.h:26
#define MAPE_GROUP(obj)
Definition: group.h:24