diff options
499 files changed, 44874 insertions, 24328 deletions
@@ -66,6 +66,7 @@ plugins/dalvik/v35/opcodes/ # GLib src/glibext/chrysamarshal.* resources.[ch] +src/gtkext/bindings/*-enums.[ch] # Binaries src/chrysalide @@ -76,6 +77,9 @@ tools/d2c/d2c tools/fuzzing/rost/fast-rost tools/yara2rost/yara2rost +# Images générées +src/data/images/about-bg.png + # Schemas src/schemas/gschemas.compiled src/schemas/*.valid @@ -83,6 +87,8 @@ src/schemas/*.valid # Misc plugins/python/androperms/androperms.db system/pkgconfig/chrysalide.pc +tools/about/bg.png +tools/about/cour10p_b.ttf tools/fuzzing/rost/rost.dict # Themes diff --git a/configure.ac b/configure.ac index 749812c..8429ae6 100644 --- a/configure.ac +++ b/configure.ac @@ -35,6 +35,10 @@ AC_PROG_INSTALL AC_PROG_MAKE_SET LT_INIT +PKG_PROG_PKG_CONFIG([0.28]) + +PKG_CHECK_VAR([GLIB_MKENUMS], [glib-2.0], [glib_mkenums]) + CFLAGS="" AC_EGREP_CPP(yes, @@ -241,6 +245,14 @@ AC_ARG_ENABLE([magic-support], AS_HELP_STRING([--disable-magic-support], [disable magic number recognition [default=no]]), [], [enable_magic_support=yes]) +AC_ARG_ENABLE([jsonglib-support], + AS_HELP_STRING([--disable-jsonglib-support], [disable jsonglib support [default=no]]), + [], [enable_jsonglib_support=yes]) + +AC_ARG_ENABLE([hs-support], + AS_HELP_STRING([--disable-hs-support], [disable hs number recognition [default=no]]), + [], [enable_hs_support=yes]) + AC_ARG_ENABLE([python-bindings], AS_HELP_STRING([--disable-python-bindings], [disable Python bindings [default=no]]), [], [enable_python_bindings=yes]) @@ -324,7 +336,7 @@ WARNING_CFLAGS="-Wall -Wimplicit -Wreturn-type -Wunused -Wswitch -Wcomment -Wuni # _BSD_SOURCE: htobe64, be64toh # _XOPEN_SOURCE: strdup, snprintf # _ISOC99_SOURCE: INFINITY; NAN -# _GNU_SOURCE: strcasestr +# _GNU_SOURCE: asprintf, strcasestr # GTK_DISABLE_DEPRECATED: on reste conforme au C99 #COMPLIANCE_FLAGS="-D_BSD_SOURCE -D_GNU_SOURCE -DGTK_DISABLE_DEPRECATED" COMPLIANCE_CPPFLAGS="-D_DEFAULT_SOURCE -D_GNU_SOURCE" @@ -369,6 +381,10 @@ else libgobj_version='-' fi +AC_SUBST(LIBGOBJ_CFLAGS) +AC_SUBST(LIBGOBJ_LIBS) + + GLIB_GSETTINGS PKG_CHECK_MODULES(LIBGTHREAD,gthread-2.0 >= 2.66.8,[libgthread_found=yes],[libgthread_found=no]) @@ -434,6 +450,18 @@ else fi +PKG_CHECK_MODULES(LIBGIOUNIX,gio-unix-2.0 >= 2.74.6,[libgiounix_found=yes],[libgiounix_found=no]) + +if test "$libgiounix_found" = "yes"; then + libgiounix_version=`pkg-config gio-unix-2.0 --modversion` +else + libgiounix_version='-' +fi + +AC_SUBST(LIBGIOUNIX_CFLAGS) +AC_SUBST(LIBGIOUNIX_LIBS) + + #--- Checks for libxml2 PKG_CHECK_MODULES(LIBXML,libxml-2.0 >= 2.6.11,[libxml_found=yes],[libxml_found=no]) @@ -569,10 +597,66 @@ if test "$libhs_found" = "yes"; then libhs_version=`pkg-config libhs --modversion` else libhs_version='-' + enable_hs_support='' fi -AC_SUBST(LIBHS_CFLAGS) -AC_SUBST(LIBHS_LIBS) +AM_CONDITIONAL([BUILD_HS_SUPPORT], [test "x$enable_hs_support" = "xyes"]) + +if test "x$BUILD_HS_SUPPORT_TRUE" = "x"; then + # Hyperscan support is available and enabled + CPPFLAGS="$CPPFLAGS -DINCLUDE_HS_SUPPORT" +fi + +if test "x$enable_hs_support" = "xyes"; then + + AC_SUBST(LIBHS_CFLAGS) + AC_SUBST(LIBHS_LIBS) + + true # empty if/then body not allowed + +fi + + +#--- Checks for json-glib-1.0 + +AM_CONDITIONAL([BUILD_JSONGLIB_SUPPORT], [test "x$enable_jsonglib_support" = "xyes"]) + +if test "x$BUILD_JSONGLIB_SUPPORT_TRUE" = "x"; then + # json-glib support is available and enabled + CPPFLAGS="$CPPFLAGS -DINCLUDE_JSONGLIB_SUPPORT" +fi + + +PKG_CHECK_MODULES(LIBJSONGLIB,json-glib-1.0 >= 1.6.6,[libjsonglib_found=yes],[libjsonglib_found=no]) + +if test "$libjsonglib_found" = "yes"; then + libjsonglib_version=`pkg-config json-glib-1.0 --modversion` +else + libjsonglib_version='-' +fi + +if test "x$enable_json_glib_support" = "xyes"; then + + AC_SUBST(LIBJSONGLIB_CFLAGS) + AC_SUBST(LIBJSONGLIB_LIBS) + + true # empty if/then body not allowed + +fi + + +#--- Checks for libzip + +PKG_CHECK_MODULES(LIBZIP,libzip >= 1.7.3,[libzip_found=yes],[libzip_found=no]) + +if test "$libzip_found" = "yes"; then + libzip_version=`pkg-config libzip --modversion` +else + libzip_version='-' +fi + +AC_SUBST(LIBZIP_CFLAGS) +AC_SUBST(LIBZIP_LIBS) #--- Checks for Python @@ -660,6 +744,8 @@ AC_CONFIG_FILES([stamp-h po/Makefile.in], [echo timestamp > stamp-h]) AC_CONFIG_COMMANDS([marshal], [echo -e "VOID:UINT64\nVOID:INT,UINT64,INT\nVOID:OBJECT,OBJECT\nVOID:ENUM,OBJECT\nVOID:ENUM,ENUM\nVOID:BOOLEAN,UINT64\nVOID:BOOLEAN,ULONG,ULONG\nVOID:INT,INT\nVOID:OBJECT,BOOLEAN\nVOID:ULONG,BOOLEAN\nVOID:DOUBLE,DOUBLE" > src/glibext/chrysamarshal.list]) +AC_CONFIG_COMMANDS([about], [./tools/about/gen.sh $version], [version=r$PACKAGE_VERSION]) + AC_CONFIG_FILES([Makefile doc/Makefile pixmaps/Makefile @@ -754,6 +840,7 @@ AC_CONFIG_FILES([Makefile plugins/pychrysalide/gui/panels/Makefile plugins/pychrysalide/mangling/Makefile plugins/pychrysalide/plugins/Makefile + plugins/pynb/Makefile plugins/python/Makefile plugins/python/abackup/Makefile plugins/python/apkfiles/Makefile @@ -799,16 +886,20 @@ AC_CONFIG_FILES([Makefile src/arch/operands/Makefile src/common/Makefile src/core/Makefile + src/data/Makefile + src/data/images/Makefile src/debug/Makefile src/format/Makefile src/glibext/Makefile src/glibext/generators/Makefile src/glibext/options/Makefile src/gtkext/Makefile + src/gtkext/bindings/Makefile src/gtkext/graph/Makefile src/gui/Makefile src/gui/core/Makefile src/gui/dialogs/Makefile + src/gui/dialogs/prefs/Makefile src/gui/menus/Makefile src/gui/panels/Makefile src/gui/tb/Makefile @@ -852,6 +943,7 @@ echo The thread support for GLib.................. : $libgthread_version echo The dynamic module loader for GLib........... : $libgmod_version echo The GNU Image Manipulation Program Toolkit... : $libgtk_version echo The GNU Image Manipulation Program Toolkit.4. : $libgtk4_version +echo The GIO UNIX specific APIs................... : $libgiounix_version echo The XML C parser and toolkit of Gnome........ : $libxml_version echo The flexible interface for archives I/O...... : $libarchive_version echo The small, fast and reliable database engine. : $libsqlite_version @@ -860,6 +952,8 @@ echo The client URL library....................... : $libcurl_version echo The YAML support library..................... : $libyaml_version echo The magic number recognition library......... : $libmagic_version echo The high-performance matching library........ : $libhs_version +echo The JSON Parser for the GLib library......... : $libjsonglib_version +echo The library for handling ZIP archives........ : $libzip_version echo echo Available Python programming language........ : $python3_version @@ -887,6 +981,12 @@ else disable_magic_support="yes" fi +if test "x$enable_hs_support" = "xyes"; then + disable_hs_support="no" +else + disable_hs_support="yes" +fi + if test "x$enable_python_bindings" = "xyes"; then disable_python_bindings="no" else @@ -899,6 +999,7 @@ echo Consider local resources..................... : $with_local_resources echo Disable GTK support.......................... : $disable_gtk_support echo Disable cURL support......................... : $disable_curl_support echo Disable Magic support........................ : $disable_magic_support +echo Disable Hyperscan support.................... : $disable_hs_support echo Disable Python bindings...................... : $disable_python_bindings echo Build a Python binary distribution........... : $build_python_package diff --git a/data/images/binfile-symbolic.svg b/data/images/binfile-symbolic.svg new file mode 100644 index 0000000..4e18f54 --- /dev/null +++ b/data/images/binfile-symbolic.svg @@ -0,0 +1,229 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="-26 -28 52 52" + version="1.1" + id="svg42" + sodipodi:docname="binfile-symbolic.svg" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#"> + <sodipodi:namedview + id="namedview44" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0" + inkscape:pagecheckerboard="true" + inkscape:deskcolor="#d1d1d1" + showgrid="true" + inkscape:zoom="15.662673" + inkscape:cx="23.303813" + inkscape:cy="25.155349" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="g1230"> + <inkscape:grid + type="xygrid" + id="grid282" + originx="26" + originy="26" /> + </sodipodi:namedview> + <defs + id="defs30"> + <clipPath + id="b"> + <circle + cy="-15" + r="9.5" + id="circle2" /> + <circle + cy="-15" + r="9.5" + transform="rotate(120)" + id="circle4" /> + <circle + cy="-15" + r="9.5" + transform="rotate(240)" + id="circle6" /> + </clipPath> + <mask + id="a" + width="60" + height="60" + x="-30" + y="-30" + maskUnits="userSpaceOnUse"> + <path + fill="#fff" + d="M-27-27h54v54h-54z" + id="path9" /> + <path + d="M2-23v-4h-4v4M-.5-6v4h1v-4" + id="path11" /> + <circle + cy="-15" + r="10.5" + id="circle13" /> + <g + transform="rotate(120)" + id="g19"> + <path + d="M2-23v-4h-4v4M-.5-6v4h1v-4" + id="path15" /> + <circle + cy="-15" + r="10.5" + id="circle17" /> + </g> + <g + transform="rotate(240)" + id="g25"> + <path + d="M2-23v-4h-4v4M-.5-6v4h1v-4" + id="path21" /> + <circle + cy="-15" + r="10.5" + id="circle23" /> + </g> + <circle + r="3" + id="circle27" /> + </mask> + </defs> + <metadata + id="metadata46"> + <rdf:RDF> + <cc:Work + rdf:about="" /> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="g1230" + inkscape:label="file" + style="display:inline" + sodipodi:insensitive="true"> + <rect + style="display:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" + id="rect4363" + width="38" + height="48" + x="-19" + y="-26.000002" + rx="0.99999994" + ry="1" + inkscape:label="border ext" /> + <rect + style="display:none;fill:#ffa700;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" + id="rect4521" + width="34" + height="44" + x="-17" + y="-24" + rx="1" + ry="0.99999994" + inkscape:label="border int" /> + <rect + style="display:none;fill:#4eff00;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" + id="rect4575" + width="10" + height="12" + x="9" + y="-26" + inkscape:label="corner ext" /> + <rect + style="display:none;fill:#00ffc4;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" + id="rect3694" + width="8" + height="10" + x="11" + y="-26" + inkscape:label="corner int" /> + <path + id="rect4765" + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" + inkscape:label="border" + d="m -18,-26 c -0.553998,0 -1,0.446002 -1,1 v 46 c 0,0.553998 0.446002,1 1,1 h 36 c 0.553998,0 1,-0.446002 1,-1 v -35 h -2 v 33 c 0,0.553998 -0.446002,1 -1,1 h -32 c -0.553998,0 -1,-0.446002 -1,-1 v -42 c 0,-0.553998 0.446002,-1 1,-1 H 9 v -2 z" /> + <path + id="rect4774" + style="display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" + inkscape:label="corner" + d="m 9,-26 v 12 h 10 v -2 h -8 v -10 z" /> + <path + id="rect1239" + style="stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + d="m 11,-26 8,10 -1,2 -8,-10 z" + sodipodi:nodetypes="ccccc" + inkscape:label="top right" /> + </g> + <g + inkscape:groupmode="layer" + id="layer5" + inkscape:label="lens" + style="display:inline"> + <circle + style="display:none;fill:#1600ba;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + id="path410" + cx="-2" + cy="-1.5" + r="10.5" + inkscape:label="circle ext" /> + <circle + style="display:none;fill:#ba00b9;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + id="path2870" + cx="-2" + cy="-1.5" + r="9.5" + inkscape:label="circle int" /> + <path + id="circle2925" + style="fill:#000000;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + inkscape:label="circle" + d="M -2 -12 A 10.5 10.5 0 0 0 -12.5 -1.5 A 10.5 10.5 0 0 0 -2 9 A 10.5 10.5 0 0 0 8.5 -1.5 A 10.5 10.5 0 0 0 -2 -12 z M -2 -11 A 9.5 9.5 0 0 1 7.5 -1.5 A 9.5 9.5 0 0 1 -2 8 A 9.5 9.5 0 0 1 -11.5 -1.5 A 9.5 9.5 0 0 1 -2 -11 z " /> + <path + id="rect3512" + style="stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + d="M 5,5 15,14 13,16 4,6 Z" + sodipodi:nodetypes="ccccc" /> + </g> + <g + inkscape:groupmode="layer" + id="layer1" + inkscape:label="binary" + style="display:inline"> + <text + xml:space="preserve" + style="font-size:10.6667px;display:none;fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" + x="-8.5920248" + y="2.3133454" + id="text1425" + inkscape:label="orig"><tspan + sodipodi:role="line" + id="tspan1423" + x="-8.5920248" + y="2.3133454" + style="font-size:10.6667px;stroke:#000000;stroke-opacity:1">0 1</tspan></text> + <g + aria-label="0 1" + id="text3453" + style="font-size:10.6667px;display:inline;fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round" + inkscape:label="paths"> + <path + d="m -3.0133405,-1.5053333 q 0,1.22667054 -0.2560008,2.11200667 -0.2560008,0.87466943 -0.8106692,1.34400423 -0.5546685,0.4693348 -1.4720047,0.4693348 -1.2906707,0 -1.9093394,-1.0346699 -0.6080019,-1.04533663 -0.6080019,-2.8906758 0,-1.2373372 0.2453341,-2.1120067 0.2560008,-0.8746694 0.8106693,-1.3333375 0.5546684,-0.4693348 1.4613379,-0.4693348 1.280004,0 1.9093394,1.0346699 0.6293353,1.0240032 0.6293353,2.8800091 z m -4.1173464,0 q 0,1.56800496 0.3520012,2.34667408 0.3626678,0.77866912 1.2266705,0.77866912 0.853336,0 1.2160038,-0.76800242 0.3733346,-0.77866912 0.3733346,-2.35734078 0,-1.5573383 -0.3733346,-2.3360074 -0.3626678,-0.7786691 -1.2160038,-0.7786691 -0.8640027,0 -1.2266705,0.7786691 -0.3520012,0.7786691 -0.3520012,2.3360074 z" + id="path3455" /> + <path + d="M 4.0693563,2.3133454 H 3.15202 V -3.009338 q 0,-0.4586682 0.010667,-0.7360024 0.010667,-0.2773342 0.032,-0.5760018 -0.1706672,0.1706672 -0.3093343,0.2880009 -0.1386671,0.1173337 -0.3520011,0.2986676 L 1.7226822,-3.0733383 1.232014,-3.7026736 3.2906872,-5.3026786 h 0.7786691 z" + id="path3457" /> + </g> + </g> +</svg> diff --git a/data/images/locked-symbolic.svg b/data/images/locked-symbolic.svg new file mode 100644 index 0000000..1b24147 --- /dev/null +++ b/data/images/locked-symbolic.svg @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="300" + height="300" + viewBox="0 0 79.375 79.374998" + version="1.1" + id="svg341" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="locked-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview343" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="true" + inkscape:zoom="2.3450501" + inkscape:cx="87.418175" + inkscape:cy="159.05844" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" + showguides="false"> + <inkscape:grid + type="xygrid" + id="grid462" + originx="0" + originy="0" /> + </sodipodi:namedview> + <defs + id="defs338" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + id="path1942" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 39.687499 1.3229166 A 13.229166 13.229166 0 0 0 26.458333 14.552083 A 13.229166 13.229166 0 0 0 31.75 25.124564 L 31.75 14.552083 A 7.9375 7.9375 0 0 1 39.687499 6.6145832 A 7.9375 7.9375 0 0 1 47.624999 14.552083 L 47.624999 25.135416 A 13.229166 13.229166 0 0 0 52.916666 14.552083 A 13.229166 13.229166 0 0 0 39.687499 1.3229166 z " /> + <path + id="rect1950" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 26.458333 14.552083 L 26.458333 39.687499 L 31.75 39.687499 L 31.75 14.552083 L 26.458333 14.552083 z M 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 14.552083 L 47.624999 14.552083 z " /> + <path + id="rect2099" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 18.520833 35.71875 C 14.123462 35.71875 10.583333 39.258879 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 74.511953 14.123462 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251537 78.052082 68.791666 74.511953 68.791666 70.114582 L 68.791666 43.656249 C 68.791666 39.258879 65.251537 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 37.703125 52.916666 L 41.671874 52.916666 C 42.40477 52.916666 42.994791 53.506688 42.994791 54.239583 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 z " /> + </g> +</svg> diff --git a/data/images/nolock-symbolic.svg b/data/images/nolock-symbolic.svg new file mode 100644 index 0000000..ed59858 --- /dev/null +++ b/data/images/nolock-symbolic.svg @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="300" + height="300" + viewBox="0 0 79.375 79.374998" + version="1.1" + id="svg341" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="nolock-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview343" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="true" + inkscape:zoom="2.3450501" + inkscape:cx="84.006734" + inkscape:cy="159.05844" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" + showguides="true"> + <inkscape:grid + type="xygrid" + id="grid462" + originx="0" + originy="0" /> + </sodipodi:namedview> + <defs + id="defs338" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + id="path1942" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 39.687499 1.3229166 A 13.229166 13.229166 0 0 0 26.458333 14.552083 A 13.229166 13.229166 0 0 0 31.75 25.124564 L 31.75 14.552083 A 7.9375 7.9375 0 0 1 39.687499 6.6145832 A 7.9375 7.9375 0 0 1 47.624999 14.552083 L 47.624999 25.135416 A 13.229166 13.229166 0 0 0 52.916666 14.552083 A 13.229166 13.229166 0 0 0 39.687499 1.3229166 z " /> + <path + id="rect1950" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 26.458333 14.552083 L 26.458333 39.687499 L 31.75 39.687499 L 31.75 14.552083 L 26.458333 14.552083 z M 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 14.552083 L 47.624999 14.552083 z " /> + <path + id="rect2099" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 18.520833 35.71875 C 14.123466 35.71875 10.583333 39.258884 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 70.465451 10.608349 70.810037 10.652063 71.148628 L 36.380208 57.337585 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 L 41.671874 52.916666 C 42.259126 52.916666 42.753343 53.295986 42.927095 53.823587 L 68.065612 40.329321 C 66.811534 37.603321 64.061787 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 68.791666 47.447232 L 42.994791 61.294449 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 64.845137 L 14.176912 76.763789 C 15.423753 77.577929 16.914362 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251533 78.052082 68.791666 74.511949 68.791666 70.114582 L 68.791666 47.447232 z " /> + <rect + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + id="rect3055" + width="79.375" + height="6.614583" + x="-30.998156" + y="61.111198" + transform="rotate(-28.226475)" /> + </g> +</svg> diff --git a/data/images/tipoftheday-symbolic.svg b/data/images/tipoftheday-symbolic.svg new file mode 100644 index 0000000..952de70 --- /dev/null +++ b/data/images/tipoftheday-symbolic.svg @@ -0,0 +1,474 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + version="1.1" + id="svg2" + width="650" + height="650" + viewBox="0 0 650 650" + sodipodi:docname="tipoftheday-symbolic.svg" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs6"> + <inkscape:path-effect + effect="fillet_chamfer" + id="path-effect2240" + is_visible="true" + lpeversion="1" + nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,25,0,1 @ F,0,1,1,0,25,0,1 @ F,0,1,1,0,25,0,1 @ F,0,1,1,0,25,0,1 @ F,0,0,1,0,0,0,1" + unit="px" + method="auto" + mode="F" + radius="25" + chamfer_steps="1" + flexible="false" + use_knot_distance="true" + apply_no_radius="true" + apply_with_radius="true" + only_selected="true" + hide_knots="false" /> + <inkscape:path-effect + effect="fillet_chamfer" + id="path-effect2206" + is_visible="true" + lpeversion="1" + nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,0,1,0,0,0,1" + unit="px" + method="auto" + mode="F" + radius="12" + chamfer_steps="1" + flexible="false" + use_knot_distance="true" + apply_no_radius="true" + apply_with_radius="true" + only_selected="true" + hide_knots="false" /> + <inkscape:path-effect + effect="fillet_chamfer" + id="path-effect2199" + is_visible="true" + lpeversion="1" + nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,1,1,0,11,0,4 @ F,0,1,1,0,11,0,4 @ F,0,1,1,0,11,0,4 @ F,0,1,1,0,11,0,4 @ F,0,1,1,0,11,0,4 @ F,0,1,1,0,11,0,4 @ F,0,0,1,0,0,0,1" + unit="px" + method="auto" + mode="F" + radius="12" + chamfer_steps="3" + flexible="false" + use_knot_distance="true" + apply_no_radius="true" + apply_with_radius="true" + only_selected="true" + hide_knots="false" /> + <inkscape:path-effect + effect="fillet_chamfer" + id="path-effect2197" + is_visible="true" + lpeversion="1" + nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,1,1,0,31,0,1 @ F,0,1,1,0,31,0,1 @ F,0,1,1,0,31,0,1 @ F,0,1,1,0,31,0,1 @ F,0,1,1,0,31,0,1 @ F,0,1,1,0,31,0,1 @ F,0,0,1,0,0,0,1" + unit="px" + method="auto" + mode="F" + radius="31" + chamfer_steps="1" + flexible="false" + use_knot_distance="true" + apply_no_radius="true" + apply_with_radius="true" + only_selected="true" + hide_knots="false" /> + <inkscape:path-effect + effect="fillet_chamfer" + id="path-effect2138" + is_visible="true" + lpeversion="1" + nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,0,1,0,0,0,1" + unit="px" + method="auto" + mode="F" + radius="12" + chamfer_steps="1" + flexible="false" + use_knot_distance="true" + apply_no_radius="true" + apply_with_radius="true" + only_selected="true" + hide_knots="false" /> + <inkscape:path-effect + effect="fillet_chamfer" + id="path-effect2136" + is_visible="true" + lpeversion="1" + nodesatellites_param="F,0,0,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,1,1,0,12,0,1 @ F,0,0,1,0,12,0,1" + unit="px" + method="auto" + mode="F" + radius="12" + chamfer_steps="1" + flexible="false" + use_knot_distance="true" + apply_no_radius="true" + apply_with_radius="true" + only_selected="true" + hide_knots="false" /> + <inkscape:path-effect + effect="fillet_chamfer" + id="path-effect1898" + is_visible="true" + lpeversion="1" + nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" + unit="px" + method="auto" + mode="F" + radius="23" + chamfer_steps="1" + flexible="false" + use_knot_distance="true" + apply_no_radius="true" + apply_with_radius="true" + only_selected="true" + hide_knots="false" /> + <inkscape:path-effect + effect="fillet_chamfer" + id="path-effect1874" + is_visible="true" + lpeversion="1" + nodesatellites_param="F,0,0,1,0,25,0,1 @ F,0,0,1,0,25,0,1 @ F,0,0,1,0,25,0,1 @ F,0,0,1,0,25,0,1 @ F,0,0,1,0,25,0,1 @ F,0,0,1,0,25,0,1 @ F,0,0,1,0,25,0,1" + unit="px" + method="auto" + mode="F" + radius="25" + chamfer_steps="1" + flexible="false" + use_knot_distance="true" + apply_no_radius="true" + apply_with_radius="true" + only_selected="false" + hide_knots="false" /> + </defs> + <sodipodi:namedview + id="namedview4" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + showgrid="true" + inkscape:zoom="1.0728017" + inkscape:cx="78.299652" + inkscape:cy="318.79144" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer4"> + <inkscape:grid + type="xygrid" + id="grid2007" + originx="93.66602" + originy="-10.000001" /> + </sodipodi:namedview> + <g + inkscape:groupmode="layer" + inkscape:label="Image" + id="g8" + style="display:none" + transform="translate(93.66602,-9.9999993)"> + <image + width="682.66669" + height="682.66669" + preserveAspectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI +WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wgSEzkfeDBN6QAAABl0RVh0Q29tbWVudABDcmVhdGVk +IHdpdGggR0lNUFeBDhcAACAASURBVHja7d17kJ51fTf+9+69u9mwJCGQkAOQkJNyCCeDKAcFKYhA +qVjrqTzO01bpo/SHlbHaw2+m40zbcXymHTv4WIGn0+oAPhUFLNF44AEJkBKQEEIggGVJSCAJOWeT +TTa72c3vj3v9CTbAJtnsdd33/XrN7ERH4bryub/3ft/X9/oeEgAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAADgNzUpAdTl97o5SWuStiTtSUYnGTX4n3/135NkIMme1/yzvUl6Bv/81U/fa/7s +H/xn9ikzCADAyH5nK6/p3Ecn6UgydvDnqMH/7XDrT7IjybYk25PsTLJrMEz8KigAAgAwxO9jy2s6 +9yMGO/cxgx37uMHOv1Z0JdmUZMtgWNg1OJIgHIAAAA2pZbBjP2bwZ9zgT6N8H/uSrEuyYXAE4VfB +ABAAoG40D3b245NMSTK1xp7iR9KWJOsHRw26Up2LAAgAUDMd/hFJjk4ye/BPDt7GJGsGQ8HOVCcg +AgIAlEJHkklJZgw+6XN4A8HKwT93KwcIADDST/ljk5ww+JRvSL8YewfDwJpU5xIYHQABAIZd2+BT +/uxUJ+5RztGBF1KdXNinHCAAwMFqTXJskpNTXYZH7diW5LkkrwoDIADAULS8ptP3Pr8+bEnyfKor +DOxBAAIAvO57MG6w0z9OOeray4NhYFtsZ4xffNCw2lOduX9STORrNH1JViR5KTYhQgCAhtCc6mS+ +01KdyQ8bkzyd6qsCowIIAFBnWpOcmGSup33ewN4kz6S6tHCvciAAQG07crDTP14pOAAvJnk2NhtC +AICaa9fHJjlrMADAwdqU5MlUJw2CAAAlbs8TkpyTZLRyMIy6kixNdb4ACABQonY8Ncm8VHfsg8Nl +R5LHk2xWCgQAKLb9npjkzJjYx8jameSxVFcOgAAAI2hyknelOrsfitI1OCIgCCAAwGF2dJJ3JzlC +KSiRjUl+kWSXUiAAwPAam+rkPofyUGYvJnkq9hFAAIBD1pLqcr7pSkGN2DcYAl6InQURAOCgHJfq +cL92Si3ak+SRVPcSAAEAhuDIJOcnGaMU1IENSR4dDAQgAMB+VFJd0jdDKahDK1LdXthrAQQAeI0T +Up3kp01Sz3YleTjV5YMgANDQWpOcl2SiUtBAOpMsSzKgFAgANKLJqb7r1w5pRHuSLIpNhBAAaCAt +qc7un6wUkF8mWR5zAxAAqHMTk7wnSbNSwP9vd5IHknQrBQIA9aaS6iS/45TiMBe6UmmqVCppbW1t +Hj16dPOYMWMqo0ePrkycOLFt5syZR86aNeuo6dOnHz158uRjWlpampOkt7d374YNG7a+/PLLW199 +9dVdW7du7dm0adOezZs3927ZsqV348aNfV1dXf39/f2eUg+vJ1LdTRAEAOpCR5KLk4xSioPX3t7e +PGXKlLZp06aNnjVr1pg5c+YcNWPGjAmTJk06evTo0aPb29uPGD169OE+I2Hf1q1bN2/evHnrunXr +tq5Zs2brypUrt3d2du5cs2ZNz4YNG/p6enpMbDs0m1JdKWA7YQQAatpxSc5VhqFra2trmjlz5uh5 +8+aNP/vssyeffvrpJ06dOvW45ubmmjjyeGBgYGDr1q2bVq1ate6ZZ55Z+8QTT2x65plndrzyyiu9 +RhCGrD/Jz5NsUwoEAGqxbZ0de/i/qUql0jRnzpzRF1988aRzzjnnhLlz584aM2ZMXR54NDAwMPDi +iy92Pvjgg7+877771i1dunSnQPCWnk7ynDIgAFAr2pO8L9Whf/bT4b/vfe879tJLLz3p5JNPfntz +c3NDTojcu3dv36JFi564/fbbn3344Ye3CQNvaH2S/4g9AxAAKDlr+3/DrFmzRl911VXHXXnlladP +mzbNiMh+7N69u/vmm2/+2S233PKSILBfPUnuS3W1AAgAlM4ZSeY0ehHa29ub3/Oe94z/0Ic+NPuC +Cy44c9SoUUdoGkPT3d294wtf+MKdP//5z7eqxn+xL9WNg9YrBQIAZWpH5yWZ0sid/qWXXnrMNddc +c9pZZ511uu/WoVmwYMGDN9xwwxKV2K+nUt08CAQACtWS6hK/sY32F29ra2u67LLLJuj0D4/HHnvs +iU9+8pMLVWK/1iR5LHYPRACgIKOTXJIGWt9fqVSa3vve9x517bXXnnXWWWed1qgT+EbKT3/604c+ +97nPPa4S+7UlycJUlwyCAMCIOSrJbzVKGxo7dmzlmmuuOeFTn/rU++p1mV5Z/dmf/dlt8+fP36gS ++9WT5GdJepUCAYCRMC3VbX3r3hlnnHHk5z//+TPPO++8d/rYi7F79+7uc84553/39vYa7t6/vYMh +YJdScCAqSsABmpnqBj91q62tremzn/3szJtuuunD11xzzbknnHCC8wsK1Nra2tbU1LRx8eLFjs3d +v+Yks5K8kuoRw2AEgGH39iSn1etfrr29vfm6666b+Yd/+IeXtrW1tfu4yzUKMG/evP9tj4C3tDCJ +1yUMSYsSMEQnJzm1Hv9iEyZMaL3hhhtO/t3f/d2LamW//UYzevTojrlz53YsW7Zsp2q8qQtT3Stg +nVIgADAcTht8+q8rU6ZMafv7v//7884+++yzfMTld9VVV52wbNmyZ1XiLZ2f6tbBa5UCAYBDUXe7 ++40dO7by13/912dcddVV743XYLXTEM844/gkAsDQnJfk0VT3CwABgAN2dpIT6+Uv09bW1vSlL33p +pGuuueZSQ/21Z/r06SZjHph3Df4pBCAAcEDOrJfOv1KpNF1//fWzPv3pT1/W2tra5qOtTR0dHU6X +PLgQsC/Jy0qBAMBQvD3J7Hr4i5x//vnjvva1r109bty4o32sNc/rmoPz7lgdgADAEJyYOljqN378 ++JabbrrpojPPPPM0H2l92LFjR5cqHLQLUz1O2CmLCADs15TU+CY/lUql6U//9E9nXXvttVd4z19f +XnrpJbPaD83Fqe4YuEMpEAB4raNTXT5Uswz317cnn3zSe+xD05Tk0iQLUj1DAAEA0pHkfbX81P+P +//iPZ7///e+/wEdZv/793//dbPZD15zk/YMhYK9yCAA0trbBXwg1OcHqbW972+hvfetbv3vMMccc +66OsX7t37961YsWKbpUYtu/8byW5N8mAcggANO7TwMWpwUOhKpVK01/+5V+e9MlPfvIDPsb6961v +fes+VRhWY5Kcm+q2wTQoy2oa27lJam5zlRkzZrR/+9vf/tCkSZMm+wjr3549e3rOOeecm3t6ejyt +Dr//TLJMGRr3CZDGNKcWO/+PfvSjUxYsWPA/dP6N46tf/eo9Ov/D+nvgeGUwAkDjmJDkolq64Uql +0vTNb37z3AsvvPBdPr7GsXTp0qc+/vGPG/4//H4aywMFAOpee5Ira+mznzBhQuv3vve9q6dOnepJ +pYGsXbt2zSWXXHJnf3//PtU47PqS/ChWBjQUrwAa7/P+rVrq/M8///xx999//6d1/o1lzZo1L112 +2WU6/5HTmsTpmAIAdeydSUbXys1++ctfPvVf/uVf/mjUqFHtPrrGcf/99z9y2WWX3d3b26vzH1lH +J5mrDI3DMsDGMSXJCbVwo5VKpemf//mfzz/vvPPe6WN7Y729vT1r165dt2bNmo2rVq3a8sILL2xb +tWrVrt27d/fv3Lmzv6+vb2DXrl0Dvb29Awfamba1tTW1tbU1jxkzptLa2to0adKkUTNnzjxy9uzZ +R5144onHnHDCCZMmTZo0pbm5edgeIrZv3771i1/84r8vXLjQfvXFeXuSV5JsUYr6Z7inMbQm+Z1a ++Lzb2tqa7rrrrivnzJkzx8dWNTAwsPfFF19c+fjjj69avHjx+ieffLJrw4YNfWUYHp8wYULrzJkz +R59++ulHzZ0799iTTz75+OOPP/74lpaW1qH+O1atWrXypptueuSee+7ZYMi/FPqTzI/5AAIAdeHi +VIf3Sm3s2LGVBQsWfHTixIkNvcRv69atmx944IFlCxcufOXJJ5/sWrduXW+t/R3Gjh1bmTp16qgp +U6aMmjZtWsfkyZOPGDt2bHtbW1tl27Ztu9euXdu9fPnybU8//XS3JX6ltCXJ/cogAFDb5iQ5o+w3 +OWPGjPY777zzv3V0dIxpwCf8/mefffaX995773M/+MEP1tZih09dWpbqRkHUKXMA6tuYWuj8zzjj +jCO/853v/MGBDBvXuu7u7h3z589f/N3vfnfl888/v8vQN2X8aiZZm8QZDEYAqDHNSa5Idd2/zr8E ++vr6eh966KElt91227OLFi3arolSA3Yl+XESAdUIADXkpLJ3/mefffaYW2+99Q+am5vrth0ODAwM +LFq06PF//dd/fXrx4sVdnvSpMUckOTnJCqUQAKidL+0pOv/idHd377zrrrsevvHGG3/Z1dXVr0lS +w05J8lK8Cqg7XgHUp0uSHKXzH3lr1qx56ZZbbnnkzjvvXO9pnzqyM9XzArRpIwCU2All7vzPOeec +sd/+9rf/cDg3kCmDhQsXPvrVr351aWdn525NkDp0ZJKZSTqVwggA5Q10v5OSbvE8bdq0UQsWLPh0 +a2trW70UfOnSpcu+9KUvLVq9evUezY8GcE8Sy1QFAEroXSnpdr9jx46tPPDAA3/Q0dExVscPNevV +JA8pQ/08MVIfxpe1829vb2/+yU9+8vF66PyXLFmy7C/+4i90/DSqSanuKuqsAAGAEnl3GW+qUqk0 +3XnnnVccc8wxx9ZycTdv3rzhi1/84g+t34ecn+SHMSFQAKAUpiTpKOON3Xrrre+bPXt2zR7s09fX +13vjjTf+8JZbbnlJM4MkyagkM5K8qBQCAMVqSnJ2GW/sy1/+8qnz5s07o1YL+8gjjyz53Oc+t8g6 +fvgvzkp1bwDfDQGAAk0bTOSlcuGFF47/xCc+8f5aLOjGjRvXf/azn71n+fLlNj6BN37wmJvqgUHU +8IdI7WpO8sEklTLd1Pjx41sWLlx47ahRo9prraDz589f+Od//udLbeIDQ/vKJDEh1ggABZhdts6/ +Uqk03XXXXVfXWuff3d3ddd11131/8eLFJvnB0J2VZLEyCACM/Gd3Wtlu6p/+6Z/eNXXq1BNqqZAP +PfTQY9ddd91/9Pb2euqHA3N8qmeP7FIKAYCRc1JK9grnmmuuOe6iiy46t1YKuGfPnp4vfOELd9x7 +772bNSc4pFGARcpQe8wBqE2VJFeX6fMbP358y8MPP/yZlpaW1loo4MaNG9dfffXV39+0aVOf5gSH +7EdJnINRY5qVoCZNL1t4u+222z5QK53/kiVLll144YX/pvOHYXO6EggAHH5NZfuyffrTn55WK5v9 +3HrrrT/5/d///fvN8odhdUKSdmWovc6E2jI5yQVluZlaGfrv7e3tue666/7PQw89tE0TgsPixSRP +KEPtMAmw9ryjTDdz++23X1H2zn/79u1br7jiitsN+cNhNTPJU0n2KkVt8AqgtoxPdclNKfzxH//x +9FmzZs0qc8E2bty4/pJLLrlV5w8jYroS1A6vAGrLRUkmlOFG2tvbm5csWfLZlpaWtrIWa+3atS9f +fvnld/b09AxoOjAi+pP8IE4KNALAsBpdls4/Sb72ta+dU+bOv7Ozs/Oyyy77vs4fRlSlTL+nEADq +xYlluZEZM2a0X3zxxaXd8Gf58uXPXHXVVfPt7AeFOEsJBACGT1OqO/+Vwje+8Y1Ly1qop556avnv +/d7v/cwyPyjM2FRHLBEAGAbjUpJDfy6//PIJs2bNml3GInV2dnZ+/OMfv09zgcLNUILaeLKk/N6d +6qEbhapUKk2PPPLIfx83btz4shVocLb/d73zh1IwGdAIAMPR75ah80+ST33qU9PK2Plv3759y2WX +XXaHzh9K9XtrvDIIAByayaX4NlcqTZ/5zGcuKVtx9uzZs/uDH/zg/+nu7u7XVKBUTlECAYBDM7cM +N/GRj3xkckdHx9gyFWZgYKD/Qx/60LfXrVvXq5lAKR9e7DYrAHCQRiUZU4Yb+fznP1+6mf9/9Vd/ +9d3Ozk5HkEJ5HaMEAgAHZ1IZbuKqq66aOH78+FJ9ke+666777r777lc1ESi1tylBeVkFUG6/lRJM +pFm4cOHHJk+ePLUsRVm5cmXnBz7wgXs0D6gJd6e6KgAjAAxRSxk6//e85z1Hlanz37NnT89HP/rR +H2keUDOOVgIBgANTiiU0N9xww7vLVJQbbrjhu11dXZ4moHbMUQIBgAMzs+gbGDt2bOXUU089uSwF +eeCBBx657777tmgaUFOmxutmAYAha0oJNv+59tprZ5alIN3d3Tuuv/76RzUNqElHKoEAwBAfvsuQ +mD/xiU9cUJaCXH/99d9zuh/UrMlKIAAwNMcVfQNnn332mDFjxhxVhmIsWbJk2aJFi7ZrFlCzZiuB +AMDQnFj0DfzJn/zJGWUoxMDAwN7PfOYzCzUJqGkdsSugAMBbqiQ5osgbaG9vb373u989rwzFuPnm +m39s1j/UhXFKIADw5gqfLHP55ZdPbG5uLrxtdHd3d33961/v1CSgLpgHIADwFiYWfQMf/OAH316G +QnzlK1/5UX9/v4l/UB+mK4EAwJubVuTFK5VK0zvf+c7Tii7Cxo0bX/3e9763XnOAunGEPkcA4I01 +peBtM08//fQjW1pa2oouxD/8wz/cpzlAXYYABAD2Y3TRN/DhD3/4xKLvYceOHdvuueeeDZoD1B3n +AggAlPXL8f73v/8dRd/DN77xjf/r3T/UpalKIACwf5OKvPiUKVPaxo0bV2gIGRgY2Hv77be/rClA +XbISQACgjAHg4osvLnwFwo9+9KNFtvyFutWi3xEA+K+aUvAEmfe+970nFF2EG2+88RlNAeraKCUQ +AHi9wmfen3XWWScVef1XX311/erVq/doClDXnAwoAPAbCn367+joqIwbN258kffwb//2b4s1A6h7 +45VAAOD1xhZ58TPPPLPwVH7HHXeY/Af1b6ISCAC83oQiL37RRRdNKfL6GzduXL9p06Y+zQD8rkMA +aDSFrgA499xzCz2v+957712mCUBDaE110jMCACnBCoAZM2acWOT1f/CDH6zWDKBhVJRAAKCqpciL +d3R0VFpaWlqLuv7AwED/008/3a0ZQEONAiAAUPSXYfr06e1FXv/pp59eYetfaCjtSiAAUFXoHgCn +nnrqmCKvv2DBguc1AWgoTgUUABhU6CmAJ510UqH7/997771O/gMjAAgA0vBIO/nkkws7oGNgYGBg +3bp1vZoAeOhBAGhEHUVefObMmccVde3Vq1e/5P0/CAAIAI2q0F0Ax48fX9jGHCtWrFjj44eGYw6A +AMCgcUVduK2trdANOZ577rnNPn4QABAAGlVhw2EdHR2FbsjxwgsvdPn4QQBAAPAZjLCjjz660D0I +1q1b5/hfaDxNsR2wAECxW2JOnDix0D0Idu7cuVcTgIYNAQgADa3QJ/BJkyaNKvL6O3bs6NcEwMMP +AkAjKvQJfNKkSYUux9m9e/eAJgANqUUJBIBGV+iOWGPGjCk0gPT19dkDAAQABICGVOhs2NbW1kLb +gE2AoGG1KYEA0OgK3QWwpaVFGwAEAAGAAhR6Ep8AABTEgUACQMMrdBtgAQAoiPMABAAEAKAB2Q1Q +AGh4hW6F29LSYi0uUIQOJRAAGt2OIi++d+9eG/EACAAUoLvIi2/fvt1e/EARdimBACAANHAAqFQq +9gOHxrRbCQSARtdTcADoLfL6ra2tAgAIAAgADanQDrirq6vQ67e1tQkA0Ji8fhQAGl6hx+Fu27at +r8jrt7e3a4MgACAANKRCZ+F3dXUVGkAmTJhgO1BoTL1KIAA0ukKPw926dWuhIwCnnnrqGE0ABAAE +gEZV2FDYhg0bCg0Ab3vb247y8UND2qsEAgDJzqIu3NPTMzAwMFDYF3HOnDnH+vhBAEAAEACKGQXY +UNS1Z86ceZyPHxrSPiUQACh4M6CXX3751aKuPWnSpMk2AwIPPQgAjarQDTFWrVq1pcjrn3LKKU4F +g8bSpQQCAFWF7gbY2dm5rcjrX3jhhZM0AWgo25RAAKCq0A0xVqxYsb3I619wwQUzNAFoKNuVQACg +qtD1sCtWrCj0fdzcuXNPMg8AGoo5AAIAZRgB6Orq6t+zZ09h8xBaW1vb3v72t5sHAI2jRwkEAKoK +Xw/7/PPPdxZ5/Q9/+MPTNQNoGH1KIABQta/oL8QTTzyxpsjrX3755WdpBtAwv+8GlEEA4Nc2F3nx +xx57bEOR1z/mmGOOnTBhQqtmAHVvoxIIALze1iIvvmzZsh1FF+DKK6+0HBDq3wYlEAB4vUKXxWza +tKmvyImASfKxj33MawCof1uUQADg9bqLvoElS5Y8XeT1Z82aNbujo6OiKUBdswRQAOA37C76Bn78 +4x93Fn0PV155pdMBob7tUQIBgNfrLfoG7r///k1F38PHPvax0zQFqFt7k/QrgwDA6w2k4M0xNm3a +1Ld9+/ZCJyPOnTv3lPb2du0S6tPLSiAAsH/rir6BRYsWLS/4FpouvfTSYzQFqEvrlUAAYP8KXx/7 +k5/8ZFXR9/Dbv/3bszQFqEtOARQAeANbi76BhQsXbk11p67CzJs37xRNAerSLiUQANi/wpcC9vT0 +DDz33HPPF3kPY8aMGWc5INRl528LYAGANzBQhoR8xx13PFX0PbzjHe8YozlAXVmlBAIAb2510Tcw +f/78wifqzJ49WwCA+mICoADAWyh8JUBXV1f/6tWrXyryHqZOndqhKUBd6VICAYA3t70MN/H973// +iSKvPzDgVSHUkZ5UNwFCAOBN7E0J5gHcdttta1LgaoD169d3awpQN15UAgGAGvmydHd39y9durSw +yYDLly+3XhjqxzolEAAYmrVluImvf/3rS4q69lNPPeXEMKgf25VAAGBodqTgzXiSZNGiRdvXr18/ +4mGks7Pzhd7e3n2aAdSFjbH+XwBgyPYleakMN/K3f/u3/3ekr/nNb35zsSYAdeMFJRAAODCrynAT +99577+a1a9euGanrbd++fduCBQs2+fihrkYAEAA4AFvKciN/9Ed/9MOM0CuJv/mbv/lhf3+/4X+o +Dz1JepVBAODADKQEuwImycqVK3u+853v/OxwX2fp0qXL5s+f72kB6sezSlBeDlwpt11JZpbhRh5+ ++OFNF1100bhjjz124uH492/cuHH91Vdf/cP+/n6fOtSPJbEBkBEADsq2lGT2bH9//76PfOQjP335 +5ZeHfT7A5s2bN11xxRV3mPkPdaVn8AcBgIOwLyWaQdvf37/vyiuvvGv16tWrhuvf2dnZ+cLFF198 +e1dXl0d/qC8rlEAA4NCsLFWk7+kZ+MAHPvCDu++++/5D+fcMDAzsvfnmm+dfccUV83t6eqwRhvqz +VgnKrUkJasLvJGkr203NmDGj/e/+7u/Omzdv3hlD/Wf27t3be8899zz0la985RlP/VC3upL8TBkE +AA7dnCRnlPXmxo4dW7niiiuOveSSS2aedNJJ0ydOnDjpNU/6/WvXrl37xBNPdC5YsGDVgw8+uM0y +P6h7i2L/fwGAYdE2OApQMyqVSlNSnTfg44OGc3cSI3wlZw5AbehNsr6Wbri/v3+fzh8a0kqdvwDA +8HpaCYAa8J9KIAAwvLYl2aMMQIntSnUCIAIARgGABvKkEggAHB5rlAAoqX2psblKAgC1ZG+Sl5QB +KKHnUpKtyxEA6tUzSgCU0AtKIABweO1KskEZgBJZH5OUBQBGxDIlAErE5D8BgBGyffAHoGhbk+xU +BgGAkbNUCYASWKIEAgAja1OSHmUACrQz1U3KEAAwCgA0kMeVQACgGGtj5i1QjF2pjkQiAFCAfRI4 +UJBHlUAAoFjrYy4AMLJ2JtmsDAIAxY8C/EIZgBHkd44AQEm8mqRbGYAR0OXpXwCgXMwFAEbCYiUQ +ACiXjUl2KANwGL06OAKAAIBkDjQQI40CACW1Pck6ZQAOg1VJdiuDAEB52ZcbOBycQlpnKkpQd/Ym +aUlyjFIAw9j5b1QGIwCU3zOp7g8AcKj6krygDEYAqA37Uj0jYIpSAIdoUewzYgSAmrIytggGDk1X +kg3KIABQe6MA/6EMwCE+/SMAUIO2xLJA4OB0xtC/AEBNeywmBAIHZm8s+6t7JgHWv4FUZ/FOVgpg +iBaleuQvRgCocZ1JdikDMARbUt3zHwGAOrAvySPKAAyBycMCAHVma5LVygC8iWdj+bAAQF1akqRf +GYD96E2yQhkah0mAjWVfqicGTlMK4DcsjNP+jABQ19bFzl7A672a6uQ/BADq3COxNwDwa48qQePx +CqAxDaQ61DdVKaDh/cLTvxEAGsuqJNuUARratiQvKYMAQON5WAmgoT2kBI3LK4DGtjfVHQK9CoDG +84skm5XBCACNa1W8/4NGY+gfAYAk1VcBVgVA4zD0j1cAJKnuDrgzyfFKAXXv8Rj6xwgAr7EmTgCD +erct1dd+IADwOo+kukcAUH/2xdA/r+EVAK81kOqEwOlKAXXn0VRPBQUjAOzXq0leVgaoKxt9rxEA +GIrHkvQpA9SF/iSLlAEBgKEYiHeFUC8eTnXTL3gdcwB4I7uTtCU5WimgZq1O8p/KgBEADtSywSAA +1J6+VNf8gwDAAduX5EFlgJr0QCzr5U14BcBb6U11EtEkpYCa8ctUh//BCACH/MukSxmgJuxOslwZ +EAAYDl4FQO1YGId7MQReATBUe5P0JJmiFFBaK5K8ogwYAWC4vRiniEFZ7UzyrDIgAHC4PBzDi1BG +D/puciC8AuBADSTZkeR4pYDSWJ5kvTJgBIDD7eVUTw0Eircj1ZU6IAAwIhwuAuVg1j8HxSsADlZ/ +qpsETVYKKMzyVI/wBiMAjKjOVIcfgZHXE0P/CAAUZF+8CoCiPBRD/xwCrwA4VL2DQXKCUsCIeSnV +fTnACACFemYwCACH30CSJ5QBAYAy2JdksTLAiHgk1Um4IABQChtibwA43LYkWacMCACUzaNKAIfV +fygBAgBlLXkJqgAABzpJREFU1J3q0kBg+D2T6tI/EAAopadiaRIMt/4kzysDw8kyQIbbviR7kkxR +Chg2jybpUgaMAFB2K5PsVgYYFt1JXlEGBABqZRTgMWWAYfGIEiAAUEs2JtmpDHBItg3+gABATfmF +EsAhMZKGAEBN2hwTl+BgbfH9QQCgli1RAjgoRtAQAKj5UYBdygAHZGuSHcqAAECtW6oE4DuDAEDj +WZ9krzLAkPTEwVoIANSJfUmWKwMMyRNKgABAPVmtBDCksLxeGRAAqCd9SdYqA7yp/0wyoAwIANSb +Z5QA3pTjtBEAqEvbYzIgvJFdqR78AwIAdcmZ5rB/JsoiAFDXTAaE/TP5DwGAutadZI8ywOvsTHWi +LAgA1LVfKgG8jldjCAA0hHVKAK9j+B8BgIbgkBP4tb4ku5UBAYBGYLcz+LU1SoAAQCNZpQSQxCsx +BAAazDYlAN8FBAAazy4lgCTV439BAKBhDMTEJ9ia6pwYEABoKK8oAQ1ukxIgANCIvPvECAAIADSg +nUqA7wAIADQecwDwHQABgAbUqwQ0OAcAIQDQkPqVgAY3oAQIADQiy5/wHQABAL/8wHcABAAAQAAA +AAQAAEAAAAAEAABAAAAABAAAEACUgAI1KQG+AyAAAAACAA3ALmj4DoAAAAAIAACAAAAACAAAgAAA +AAgA1JJ+JaBBWQGAAEBD260ENKhuJUAAQAAAAQAEABrKLiVA2wcBgMazXQloUDuUAAEAvwRB+AUB +gIZiGJRGZQ4AAgACADSgHiVAAKCR9cVeADRmu9+rDAgANLpXlIAGs0oJEAAgeUkJaDBrlQABAJKN +SkCD2awECACQDCR5WRloEJ2DbR4EAEjytBLQIH6pBAgA8Gs7470o9W9trP9HAID/4gkloI7tS/IL +ZaAsKkpAiexN0ptkslJQhx5PskUZMAIA+/dCzJCm/qyJ5a4IAPCWHhocCYB6sDXJY8pA2XgFQBkN +JFmZZKY2So3bkeT+WPaHAABD1p/qsOnMGKmiNm1M8nOdPwIAHLi+VDdNOS7JKOWghqxMsjjVmf8g +AMBB+NXrgKOSjFEOSm7fYMf/vFJQdk1KQA2ZnOS8eCVAOW1KsijVkSsQAGCYtSSZl+QEpaAkdg8+ +9Vu+igAAI2B0knOSTFQKCrI31Z391sa7fgQAGHFHJpmb5HilYASf+J/U8SMAQDm0JZmW5JTB/wzD +bU2SZ5N0KQUCAJR3VGB6ktlJWpWDQ7A1yXNJ1qe6NwUIAFAj2pMcOzg64JAhhmJdqvtPbI4Z/QgA +UDftfXSScUkmJJkaewuQvJpkdarL+HbFe30EAGiY70Bbqq8Njk4yPskxSTqUpi5tT/LKYGfflWSP +Dh8BAPhNlcFwMDrJEYOjBUcOjiCM8/0prb7BDn5Dqofx7ErSk+qyPfvygwAAw/L9qaS6OVFbqucV +HDE4enBkkrGDocGW2wend7Az7x98Uh8Y7Mx/1aH/6qdvsHPfO/j/9UQPAgDU/PfztSGjdTBotKU6 +uXH0a8JGR0buwKQ9SbqT7Bz8c/dgR9z7mk77tZ2xDhkAAAAAAAAAAAAAAAAAAAAAAACgnvxPJUDb +gwPXogTU2S/iLykHh8m4JP+vMiAAQG08lQkEeMoHAQC/wIUCdPYgAOAXvVCgDYAAADqENyEg6OBB +AAAdiqCgYwcBANBBATWmWQkAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAA +BAAAQAAAAAQAAEAAAAAEAABAAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAAB +AAAQAAAAAQAAEAAAAAEAABAAAAABAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAA +AAQAAEAAAAAEAABAAAAABAAAQAAAAAQAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAA +AQAAEAAAAAEAABAAAICD1aQE1IH/qQQU4EtKgBEAAEAAAAAEAABAAAAABAAAYMRZBUC9sBKAkWQF +AEYAAAABAACoAV4BUE+8BmAkGP7HCAAAIAAAADXCKwDqjdcAHE6G/zECACX1v5QAbQsEABrPaiVA +24K35hUA9cqrAIaToX+MAAAARgDAKACe/kEAACEAnT8IACAEoPMHAQCEAHT+IACAEIDOHwQAEALQ ++YMAAEIAOn8QAEAQQMcPAgAMl2lJ/h9laEj/K7b3RQAAowFK4KkfBAAQBtDpgwAAwgA6fRAAwPcm ++aoylMqfJ9mnDCAAwEgxQuAJHwQAEALQ+UNtaFYCABAAAAABAACoR+YAwPAwD2Bkef8PAgAIATp/ +4EB5BQAARgAAowCe/kEAAIQAnT8IAIAgoOMHAQAQAnT+IAAAgoCOHwQAQBDQ8YMAAAgDOn0QAIDG +Cgc6eQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASu//AxdVgfzar9wBAAAAAElFTkSu +QmCC +" + id="image10" /> + </g> + <g + inkscape:groupmode="layer" + id="layer1" + inkscape:label="glass" + style="display:inline" + transform="translate(93.66602,-9.9999993)"> + <rect + style="display:none;fill:#0000ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + id="rect1708" + width="260" + height="100" + x="211.33334" + y="388.56705" /> + <circle + style="display:none;fill:#0000ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + id="path1809" + cx="341.33334" + cy="224.89578" + r="215" /> + <path + id="rect2235" + style="display:none;fill:#000000;fill-opacity:0.25;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + d="m 341.33398,9.8964844 c -118.7411,0 -215,96.2588956 -215,214.9999956 0.18532,58.6064 24.26094,114.20819 66.00977,154.33008 9.94954,9.56181 21.49034,27.14716 24.57421,40.60547 l 10.16602,44.36524 c 2.18556,9.53801 8.6328,17.13104 17.00977,21.17187 a 103.49999,20 0 0 0 97.24023,13.19727 103.49999,20 0 0 0 97.23437,-13.19336 c 8.38004,-4.04003 14.82953,-11.6354 17.01563,-21.17578 L 465.75,419.83203 c 3.08387,-13.45831 14.62467,-31.04366 24.57421,-40.60547 41.74883,-40.12189 65.82445,-95.72368 66.00977,-154.33008 0,-118.7411 -96.2589,-214.9999956 -215,-214.9999956 z" /> + <path + id="circle3072" + style="display:none;fill:none;stroke:#d66868;stroke-width:32;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + d="M 197.02511,265.82419 C 182.0017,212.8536 197.20659,155.90981 236.63564,117.47888 276.06469,79.047958 333.37953,65.307805 385.94759,81.684159" + sodipodi:nodetypes="csc" + inkscape:label="light" /> + <path + style="color:#000000;display:none;fill:#000000;fill-opacity:0.1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" + d="m 346.49023,58.978516 c -44.42827,-1.369495 -88.31106,15.160596 -121.02148,47.042964 -43.6139,42.50984 -60.45387,105.57529 -43.83594,164.16797 a 16,16 0 0 0 19.75781,11.02735 16,16 0 0 0 11.02735,-19.75782 C 198.98912,214.11059 212.55861,163.28944 247.80273,128.9375 283.04686,94.585563 334.20087,82.322739 381.18945,96.960937 A 16,16 0 0 0 401.22266,86.443359 16,16 0 0 0 390.70703,66.408203 c -14.53686,-4.528619 -29.40737,-6.973189 -44.2168,-7.429687 z" + id="path3152" /> + <path + id="path3210" + style="display:inline;fill:#000000;fill-opacity:0.25;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + d="m 341.33398,9.8964844 c -118.74098,0 -215,96.2590156 -215,214.9999956 0.18532,58.60635 24.26098,114.20823 66.00977,154.33008 9.94953,9.5618 21.49035,27.14717 24.57421,40.60547 l 10.16602,44.36524 c 2.18556,9.538 8.6328,17.13104 17.00977,21.17187 a 103.49999,20 0 0 0 97.24023,13.19727 103.49999,20 0 0 0 97.23437,-13.19336 c 8.38004,-4.04003 14.82953,-11.63541 17.01563,-21.17578 L 465.75,419.83203 c 3.08386,-13.4583 14.62468,-31.04367 24.57421,-40.60547 41.74879,-40.12185 65.82445,-95.72373 66.00977,-154.33008 0,-118.74098 -96.25902,-214.9999956 -215,-214.9999956 z m -3.16602,49.0351566 c 2.76991,-0.05362 5.54551,-0.03872 8.32227,0.04687 14.80942,0.456497 29.67995,2.901073 44.2168,7.429687 a 16,16 0 0 1 10.51562,20.035156 16,16 0 0 1 -20.0332,10.517579 C 334.20092,82.322754 283.04682,94.585597 247.80273,128.9375 c -35.24408,34.35191 -48.8136,85.17314 -35.38477,132.52148 a 16,16 0 0 1 -11.02734,19.75782 16,16 0 0 1 -19.75781,-11.02735 c -16.61792,-58.59262 0.22208,-121.65817 43.83594,-164.16797 30.66598,-29.889686 71.15059,-46.285463 112.69921,-47.089839 z" /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="base" + style="display:inline" + transform="translate(93.66602,-9.9999993)"> + <rect + style="display:none;fill:#ff0000;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + id="rect2005" + width="180" + height="130.64417" + x="251.33334" + y="504.35583" + ry="0" /> + <rect + style="display:none;fill:#ff0000;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + id="rect2015" + width="116.16666" + height="35" + x="283.25" + y="625" /> + <path + id="rect2201" + style="display:inline;fill:#000000;fill-opacity:0.5;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + d="M 251.33398,504.38672 V 623 a 12,12 0 0 0 12,12 H 271.25 a 12,12 0 0 1 12,12 v 1 a 12,12 0 0 0 12,12 h 92.16601 a 12,12 0 0 0 12,-12 v -1 a 12,12 0 0 1 12,-12 h 7.91797 a 12,12 0 0 0 12,-12 V 504.38672 a 103.49999,20 0 0 1 -90,10.17969 103.49999,20 0 0 1 -90,-10.17969 z" /> + </g> + <g + inkscape:groupmode="layer" + id="layer3" + inkscape:label="sep" + style="display:inline" + transform="translate(93.66602,-9.9999993)"> + <ellipse + style="display:none;fill:#ff9955;stroke-width:0;stroke-linecap:round;stroke-linejoin:round" + id="path2475" + cx="341.33334" + cy="478.56641" + rx="103.49999" + ry="20" + sodipodi:insensitive="true" /> + </g> + <g + inkscape:groupmode="layer" + id="layer4" + inkscape:label="background" + transform="translate(220,-0.10351487)" + style="display:inline"> + <rect + style="display:none;fill:#ffce09;fill-opacity:1;stroke:none;stroke-width:0.999998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect6199" + width="650" + height="650" + x="-220" + y="0.10351487" /> + <rect + style="display:none;fill:#000000;fill-opacity:0.730517;stroke:none;stroke-width:0.999998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect6987" + width="650" + height="650" + x="-220" + y="0.10351487" /> + </g> +</svg> diff --git a/data/images/unlocked-symbolic.svg b/data/images/unlocked-symbolic.svg new file mode 100644 index 0000000..da7b6ac --- /dev/null +++ b/data/images/unlocked-symbolic.svg @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="300" + height="300" + viewBox="0 0 79.375 79.374998" + version="1.1" + id="svg341" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="unlocked-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview343" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="true" + inkscape:zoom="2.3450501" + inkscape:cx="87.418175" + inkscape:cy="159.05844" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" + showguides="false"> + <inkscape:grid + type="xygrid" + id="grid462" + originx="0" + originy="0" /> + </sodipodi:namedview> + <defs + id="defs338" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + id="path1942" + style="display:none;fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 39.687499,1.3229166 A 13.229166,13.229166 0 0 0 26.458333,14.552083 V 39.687499 H 31.75 V 25.124564 14.552083 a 7.9375,7.9375 0 0 1 7.937499,-7.9374998 7.9375,7.9375 0 0 1 7.9375,7.9374998 v 10.583333 14.552083 h 5.291667 V 14.552083 A 13.229166,13.229166 0 0 0 39.687499,1.3229166 Z" /> + <path + id="rect2099" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 18.520833 35.71875 C 14.123462 35.71875 10.583333 39.258879 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 74.511953 14.123462 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251537 78.052082 68.791666 74.511953 68.791666 70.114582 L 68.791666 43.656249 C 68.791666 39.258879 65.251537 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 37.703125 52.916666 L 41.671874 52.916666 C 42.40477 52.916666 42.994791 53.506688 42.994791 54.239583 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 z " /> + <path + id="path2191" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 60.854166 1.3229166 A 13.229166 13.229166 0 0 0 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 25.124564 L 52.916666 14.552083 A 7.9375 7.9375 0 0 1 60.854166 6.6145832 A 7.9375 7.9375 0 0 1 68.791666 14.552083 L 68.791666 25.135416 L 68.791666 25.24497 C 69.221772 25.173377 69.663543 25.135416 70.114582 25.135416 L 74.083332 25.135416 L 74.083332 14.552083 A 13.229166 13.229166 0 0 0 60.854166 1.3229166 z " /> + </g> +</svg> @@ -1,4 +1,4 @@ -define([gitrepo], esyscmd([bash -c "echo -n $(( $(test -d .git && git rev-list HEAD | wc -l) + 4))"])) +define([gitrepo], esyscmd([bash -c "echo -n $(( $(test -d .git && git rev-list --count HEAD) + 4))"])) define([gitversion], ifelse(gitrepo, , 000, gitrepo)) diff --git a/pixmaps/Makefile.am b/pixmaps/Makefile.am index d1dff31..da3de19 100644 --- a/pixmaps/Makefile.am +++ b/pixmaps/Makefile.am @@ -4,19 +4,6 @@ APP_ICONS = \ chrysalide-64.png \ chrysalide-128.png -REVISION_PIX = \ - revision_0.png \ - revision_1.png \ - revision_2.png \ - revision_3.png \ - revision_4.png \ - revision_5.png \ - revision_6.png \ - revision_7.png \ - revision_8.png \ - revision_9.png \ - revision.png - TOOLBAR_BUTTONS = \ tbutton_collapse.png \ tbutton_expand.png \ @@ -41,8 +28,6 @@ OTHER_ICONS = \ breakpoint_normal.png MISC = \ - chrysalide-full.png \ - chrysalide_text.png \ welcome.png CORE = \ @@ -57,13 +42,12 @@ EXTRA_DIST = \ openida_text.xcf \ before-after.png \ $(APP_ICONS) \ - $(REVISION_PIX) \ $(TOOLBAR_BUTTONS) \ $(LIST_ICONS) \ $(ERROR_ICONS) \ $(MISC) \ $(CORE) -pix_DATA = $(APP_ICONS) $(REVISION_PIX) $(TOOLBAR_BUTTONS) $(LIST_ICONS) $(ERROR_ICONS) $(OTHER_ICONS) $(MISC) +pix_DATA = $(APP_ICONS) $(TOOLBAR_BUTTONS) $(LIST_ICONS) $(ERROR_ICONS) $(OTHER_ICONS) $(MISC) pixdir = $(pixmapsdir) diff --git a/pixmaps/chrysalide-full.png b/pixmaps/chrysalide-full.png Binary files differdeleted file mode 100644 index a153353..0000000 --- a/pixmaps/chrysalide-full.png +++ /dev/null diff --git a/pixmaps/chrysalide_text.png b/pixmaps/chrysalide_text.png Binary files differdeleted file mode 100644 index b4d8c78..0000000 --- a/pixmaps/chrysalide_text.png +++ /dev/null diff --git a/pixmaps/revision.png b/pixmaps/revision.png Binary files differdeleted file mode 100644 index 330e108..0000000 --- a/pixmaps/revision.png +++ /dev/null diff --git a/pixmaps/revision_0.png b/pixmaps/revision_0.png Binary files differdeleted file mode 100644 index 0a468de..0000000 --- a/pixmaps/revision_0.png +++ /dev/null diff --git a/pixmaps/revision_1.png b/pixmaps/revision_1.png Binary files differdeleted file mode 100644 index 738a881..0000000 --- a/pixmaps/revision_1.png +++ /dev/null diff --git a/pixmaps/revision_2.png b/pixmaps/revision_2.png Binary files differdeleted file mode 100644 index b567b80..0000000 --- a/pixmaps/revision_2.png +++ /dev/null diff --git a/pixmaps/revision_3.png b/pixmaps/revision_3.png Binary files differdeleted file mode 100644 index 7f5e3fc..0000000 --- a/pixmaps/revision_3.png +++ /dev/null diff --git a/pixmaps/revision_4.png b/pixmaps/revision_4.png Binary files differdeleted file mode 100644 index a21ccbb..0000000 --- a/pixmaps/revision_4.png +++ /dev/null diff --git a/pixmaps/revision_5.png b/pixmaps/revision_5.png Binary files differdeleted file mode 100644 index a9130d9..0000000 --- a/pixmaps/revision_5.png +++ /dev/null diff --git a/pixmaps/revision_6.png b/pixmaps/revision_6.png Binary files differdeleted file mode 100644 index 291febd..0000000 --- a/pixmaps/revision_6.png +++ /dev/null diff --git a/pixmaps/revision_7.png b/pixmaps/revision_7.png Binary files differdeleted file mode 100644 index ccd4d4e..0000000 --- a/pixmaps/revision_7.png +++ /dev/null diff --git a/pixmaps/revision_8.png b/pixmaps/revision_8.png Binary files differdeleted file mode 100644 index 054c250..0000000 --- a/pixmaps/revision_8.png +++ /dev/null diff --git a/pixmaps/revision_9.png b/pixmaps/revision_9.png Binary files differdeleted file mode 100644 index 1123669..0000000 --- a/pixmaps/revision_9.png +++ /dev/null diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 9e177fa..529f4fe 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -13,7 +13,9 @@ endif # androhelpers SUBDIRS = \ - $(PYTHON3_SUBDIRS) + $(PYTHON3_SUBDIRS) \ + pe \ + pynb # arm \ # bootimg \ diff --git a/plugins/dalvik/context.h b/plugins/dalvik/context.h index f09cfa6..1954b1e 100644 --- a/plugins/dalvik/context.h +++ b/plugins/dalvik/context.h @@ -58,7 +58,7 @@ GType g_dalvik_context_get_type(void); GDalvikContext *g_dalvik_context_new(void); /* Mémorise une zone comme étant des données de branchements. */ -bool g_dalvik_context_register_switch_data(GDalvikContext *ctx, const vmpa2t *start, phys_t length); +bool g_dalvik_context_register_switch_data(GDalvikContext *, const vmpa2t *, phys_t); /* Mémorise une zone comme étant des données d'un tableau. */ bool g_dalvik_context_register_array_data(GDalvikContext *, const vmpa2t *, uint16_t, phys_t); diff --git a/plugins/dalvik/python/module.c b/plugins/dalvik/python/module.c index d4026d9..0349a7c 100644 --- a/plugins/dalvik/python/module.c +++ b/plugins/dalvik/python/module.c @@ -42,7 +42,7 @@ * * * Paramètres : - * * * -* Description : Ajoute le module 'dalvik' au module Python. * +* Description : Ajoute le module 'dalvik' au module Python. * * * * Retour : - * * * diff --git a/plugins/dex/format.c b/plugins/dex/format.c index ccf21a8..f9a2410 100644 --- a/plugins/dex/format.c +++ b/plugins/dex/format.c @@ -70,9 +70,6 @@ static const char *g_dex_format_get_target_machine(const GDexFormat *); /* Etend la définition des portions au sein d'un binaire. */ static void g_dex_format_refine_portions(GDexFormat *); -/* Fournit l'emplacement d'une section donnée. */ -static bool g_dex_format_get_section_range_by_name(const GDexFormat *, const char *, mrange_t *); - @@ -166,8 +163,6 @@ static void g_dex_format_class_init(GDexFormatClass *klass) exe->translate_phys = (translate_phys_fc)g_exe_format_without_virt_translate_offset_into_vmpa; exe->translate_virt = (translate_virt_fc)g_exe_format_without_virt_translate_address_into_vmpa; - exe->get_range_by_name = (get_range_by_name_fc)g_dex_format_get_section_range_by_name; - } @@ -515,31 +510,6 @@ static void g_dex_format_refine_portions(GDexFormat *format) } -/****************************************************************************** -* * -* Paramètres : format = description de l'exécutable à consulter. * -* name = nom de la section recherchée. * -* range = emplacement en mémoire à renseigner. [OUT] * -* * -* Description : Fournit l'emplacement d'une section donnée. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_dex_format_get_section_range_by_name(const GDexFormat *format, const char *name, mrange_t *range) -{ - bool result; /* Bilan à retourner */ - - result = false; - - return result; - -} - - diff --git a/plugins/pe/Makefile.am b/plugins/pe/Makefile.am index e9cd482..ddc4ee4 100644 --- a/plugins/pe/Makefile.am +++ b/plugins/pe/Makefile.am @@ -39,22 +39,25 @@ endif libpe_la_SOURCES = \ core.h core.c \ - pe-int.h pe-int.c \ + format-int.h format-int.c \ format.h format.c \ - pe_def.h \ - rich.h rich.c \ - routine.h routine.c \ - section.h section.c \ - symbols.h symbols.c + pe-def.h + + +# rich.h rich.c \ +# routine.h routine.c \ +# symbols.h symbols.c + + libpe_la_LIBADD = \ $(PYTHON3_LIBADD) -libpe_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src +libpe_la_CFLAGS = $(TOOLKIT_CFLAGS) -I$(top_srcdir)/src libpe_la_LDFLAGS = \ -avoid-version \ - -L$(top_srcdir)/src/.libs -lchrysacore \ + -L$(top_srcdir)/src/.libs -lchrysacore4 \ $(RUN_PATH) $(PYTHON3_LDFLAGS) diff --git a/plugins/pe/core-int.h b/plugins/pe/core-int.h new file mode 100644 index 0000000..1d1b817 --- /dev/null +++ b/plugins/pe/core-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-int.h - prototypes internes pour l'intégration du support du format PE + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PE_CORE_INT_H +#define _PLUGINS_PE_CORE_INT_H + + +#include "core.h" + + +#include <plugins/native-int.h> + + + +/* Greffon natif pour le support du format PE (instance) */ +struct _GPePlugin +{ + GNativePlugin parent; /* A laisser en premier */ + +}; + + +/* Greffon natif pour le support du format PE (classe) */ +struct _GPePluginClass +{ + GNativePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un greffon de support PE. */ +bool g_pe_plugin_create(GPePlugin *, GModule *); + + + +#endif /* _PLUGINS_PE_CORE_INT_H */ diff --git a/plugins/pe/core.c b/plugins/pe/core.c index ddbacf5..b752735 100644 --- a/plugins/pe/core.c +++ b/plugins/pe/core.c @@ -24,35 +24,244 @@ #include "core.h" -#include <core/global.h> +//#include <core/global.h> #include <plugins/self.h> +#include "core-int.h" #include "format.h" #ifdef INCLUDE_PYTHON3_BINDINGS # include "python/module.h" #endif + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +/* Initialise la classe des greffons de support PE. */ +static void g_pe_plugin_class_init(GPePluginClass *); + +/* Procède à l'initialisation de l'interface de gestion. */ +//static void g_pe_plugin_plugin_manager_interface_init(GPluginManagerInterface *); + +/* Initialise une instance de greffon de support PE. */ +static void g_pe_plugin_init(GPePlugin *); + +/* Supprime toutes les références externes. */ +static void g_pe_plugin_dispose(GPePlugin *); + +/* Procède à la libération totale de la mémoire. */ +static void g_pe_plugin_finalize(GPePlugin *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_pe_plugin_enable(GPePlugin *); + +/* Prend acte de la désactivation du greffon. */ +static bool g_pe_plugin_disable(GPePlugin *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon de liaison Python */ +G_DEFINE_TYPE_WITH_CODE(GPePlugin, g_pe_plugin, G_TYPE_NATIVE_PLUGIN, + /*G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pe_plugin_plugin_manager_interface_init)*/); + + +NATIVE_PLUGIN_ENTRYPOINT(g_pe_plugin_new); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons de support PE. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_class_init(GPePluginClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_pe_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_pe_plugin_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = (pg_management_fc)g_pe_plugin_enable; + plugin->disable = (pg_management_fc)g_pe_plugin_disable; + +} + +#if 0 + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de gestion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface) +{ + iface->handle_native = (handle_native_plugins_cb)g_pe_plugin_handle_native_plugins_loaded_event; + +} + +#endif + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon de support PE. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_init(GPePlugin *plugin) +{ + STORE_PLUGIN_ABI(plugin); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_dispose(GPePlugin *plugin) +{ + G_OBJECT_CLASS(g_pe_plugin_parent_class)->dispose(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_finalize(GPePlugin *plugin) +{ + G_OBJECT_CLASS(g_pe_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : module = extension vue du système. * +* * +* Description : Crée un module pour un greffon de support PE. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +GPluginModule *g_pe_plugin_new(GModule *module) +{ + GPePlugin *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_PE_PLUGIN, NULL); + + if (!g_pe_plugin_create(result, module)) + g_clear_object(&result); + + return G_PLUGIN_MODULE(result); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = extension vue du système. * +* * +* Description : Met en place un module pour un greffon de support PE. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_pe_plugin_create(GPePlugin *plugin, GModule *module) +{ + bool result; /* Bilan à retourner */ + #ifdef INCLUDE_PYTHON3_BINDINGS -# define PG_REQ RL("PyChrysalide") +# define PG_REQ REQ_LIST("PyChrysalide") #else # define PG_REQ NO_REQ #endif + result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin), + "PeFmt", + "PE format support", + PACKAGE_VERSION, + CHRYSALIDE_WEBSITE("doc/formats"), + PG_REQ, + module); + return result; -DEFINE_CHRYSALIDE_PLUGIN("Pe", "PE format support", - PACKAGE_VERSION, CHRYSALIDE_WEBSITE("doc/formats"), - PG_REQ, AL(PGA_PLUGIN_INIT, PGA_CONTENT_RESOLVER)); +} +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * * -* Description : Prend acte du chargement du greffon. * +* Description : Prend acte de l'activation du greffon. * * * * Retour : - * * * @@ -60,7 +269,7 @@ DEFINE_CHRYSALIDE_PLUGIN("Pe", "PE format support", * * ******************************************************************************/ -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) +static bool g_pe_plugin_enable(GPePlugin *plugin) { bool result; /* Bilan à retourner */ @@ -77,6 +286,65 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) /****************************************************************************** * * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de la désactivation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_pe_plugin_disable(GPePlugin *plugin) +{ + bool result; /* Bilan à retourner */ + + result = true; + + return result; + +} + + + +#if 0 + +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = interface à manipuler. * +* * +* Description : Accompagne la fin du chargement des modules natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_handle_native_plugins_loaded_event(GPePlugin *plugin) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + + gstate = PyGILState_Ensure(); + + load_python_plugins(G_PLUGIN_MODULE(plugin)); + + PyGILState_Release(gstate); + +} + + + +/****************************************************************************** +* * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * content = contenu binaire à traiter. * @@ -90,7 +358,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) * Remarques : - * * * ******************************************************************************/ - +#if 0 G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) { bool test; /* Bilan des accès mémoire */ @@ -114,3 +382,7 @@ G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule } } +#endif + + +#endif diff --git a/plugins/pe/core.h b/plugins/pe/core.h index 4497cf5..5c0696f 100644 --- a/plugins/pe/core.h +++ b/plugins/pe/core.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.h - prototypes pour l'intégration du support du format PE * - * Copyright (C) 2017-2018 Cyrille Bagard + * Copyright (C) 2017-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,16 +25,18 @@ #define _PLUGINS_PE_CORE_H +#include <glibext/helpers.h> #include <plugins/plugin.h> -#include <plugins/plugin-int.h> -/* Prend acte du chargement du greffon. */ -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *); +#define G_TYPE_PE_PLUGIN (g_pe_plugin_get_type()) -/* Procède à une opération liée à un contenu binaire. */ -G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); +DECLARE_GTYPE(GPePlugin, g_pe_plugin, G, PE_PLUGIN); + + +/* Crée un module pour un greffon de support PE. */ +GPluginModule *g_pe_plugin_new(GModule *); diff --git a/plugins/pe/pe-int.c b/plugins/pe/format-int.c index 4104ce1..2d1e6c8 100644 --- a/plugins/pe/pe-int.c +++ b/plugins/pe/format-int.c @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * pe-int.c - structures internes du format Portable Executable + * format-int.c - structures internes du format Portable Executable * * Copyright (C) 2010-2017 Cyrille Bagard * @@ -21,14 +21,14 @@ */ -#include "pe-int.h" +#include "format-int.h" #include <malloc.h> #include <string.h> #include <i18n.h> -#include <common/endianness.h> +#include <common/datatypes.h> #include <core/logs.h> @@ -46,7 +46,7 @@ * * ******************************************************************************/ -bool read_dos_image_header(const GPeFormat *format, image_dos_header *header) +bool read_dos_image_header(const GPeFormat *format, image_dos_header_t *header) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -104,7 +104,7 @@ bool read_dos_image_header(const GPeFormat *format, image_dos_header *header) * * ******************************************************************************/ -bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header *header) +bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header_t *header) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -140,13 +140,13 @@ bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header * * ******************************************************************************/ -bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optional_header *header) +bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optional_header_t *header) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ - image_optional_header_32 *hdr32; /* Version 32 bits */ - image_optional_header_64 *hdr64; /* Version 64 bits */ - image_data_directory *directories; /* Répertoires à charger */ + image_optional_header_32_t *hdr32; /* Version 32 bits */ + image_optional_header_64_t *hdr64; /* Version 64 bits */ + image_data_directory_t *directories; /* Répertoires à charger */ uint32_t *number_of_rva_and_sizes; /* Quantité de ces répertoires */ uint32_t i; /* Boucle de parcours */ @@ -278,7 +278,7 @@ bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optiona * * ******************************************************************************/ -bool read_pe_nt_header(const GPeFormat *format, image_nt_headers *header, vmpa2t *next) +bool read_pe_nt_header(const GPeFormat *format, image_nt_headers_t *header, vmpa2t *next) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -318,7 +318,7 @@ bool read_pe_nt_header(const GPeFormat *format, image_nt_headers *header, vmpa2t * * ******************************************************************************/ -bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_section_header *section) +bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_section_header_t *section) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -331,7 +331,7 @@ bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_se for (i = 0; i < IMAGE_SIZEOF_SHORT_NAME && result; i++) result = g_binary_content_read_u8(content, pos, (uint8_t *)§ion->name[i]); - if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, §ion->misc.physical_address); + if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, §ion->physical_address); if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, §ion->virtual_address); if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, §ion->size_of_raw_data); @@ -361,7 +361,7 @@ bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_se * * ******************************************************************************/ -bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_export_directory *dir) +bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_export_directory_t *dir) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -401,7 +401,7 @@ bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_ * * ******************************************************************************/ -bool read_pe_image_import_descriptor(const GPeFormat *format, vmpa2t *pos, image_import_descriptor *desc) +bool read_pe_image_import_descriptor(const GPeFormat *format, vmpa2t *pos, image_import_descriptor_t *desc) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ diff --git a/plugins/pe/pe-int.h b/plugins/pe/format-int.h index ebcf3f0..0b5ad4b 100644 --- a/plugins/pe/pe-int.h +++ b/plugins/pe/format-int.h @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * pe-int.h - prototypes pour les structures internes du format Portable Executable + * format-int.h - prototypes pour les structures internes du format Portable Executable * * Copyright (C) 2009-2017 Cyrille Bagard * @@ -21,14 +21,13 @@ */ -#ifndef _PLUGINS_PE_PE_INT_H -#define _PLUGINS_PE_PE_INT_H +#ifndef _PLUGINS_PE_FORMAT_INT_H +#define _PLUGINS_PE_FORMAT_INT_H #include <format/executable-int.h> -#include "pe_def.h" #include "format.h" @@ -36,13 +35,14 @@ /* Format d'exécutable PE (instance) */ struct _GPeFormat { - GExeFormat parent; /* A laisser en premier */ + GExecutableFormat parent; /* A laisser en premier */ - image_dos_header dos_header; /* En-tête DOS */ + image_dos_header_t dos_header; /* En-tête DOS */ mrange_t rich_header; /* En-tête enrichi */ - image_nt_headers nt_headers; /* En-tête Windows */ + image_nt_headers_t nt_headers; /* En-tête Windows */ - image_section_header *sections; /* Liste des sections */ + vmpa2t sections_start; /* Début de la zone de sections*/ + image_section_header_t *sections; /* Liste des sections */ bool loaded; /* Détection partielle menée */ @@ -51,32 +51,35 @@ struct _GPeFormat /* Format d'exécutable PE (classe) */ struct _GPeFormatClass { - GExeFormatClass parent; /* A laisser en premier */ + GExecutableFormatClass parent; /* A laisser en premier */ }; +/* Met en place une nouvelle instance de format PE. */ +bool g_pe_format_create(GPeFormat *, GBinContent *); + /* Procède à la lecture d'un en-tête de programme DOS. */ -bool read_dos_image_header(const GPeFormat *, image_dos_header *); +bool read_dos_image_header(const GPeFormat *, image_dos_header_t *); /* Procède à la lecture d'un en-tête de programme PE (1). */ -bool read_pe_file_header(const GPeFormat *, vmpa2t *, image_file_header *); +bool read_pe_file_header(const GPeFormat *, vmpa2t *, image_file_header_t *); /* Procède à la lecture d'un en-tête de programme PE (2). */ -bool read_pe_optional_header(const GPeFormat *, vmpa2t *, image_optional_header *); +bool read_pe_optional_header(const GPeFormat *, vmpa2t *, image_optional_header_t *); /* Procède à la lecture d'un en-tête de programme PE. */ -bool read_pe_nt_header(const GPeFormat *, image_nt_headers *, vmpa2t *); +bool read_pe_nt_header(const GPeFormat *, image_nt_headers_t *, vmpa2t *); /* Procède à la lecture d'un en-tête de section PE. */ -bool read_pe_image_section_header(const GPeFormat *, vmpa2t *, image_section_header *); +bool read_pe_image_section_header(const GPeFormat *, vmpa2t *, image_section_header_t *); /* Procède à la lecture d'un répertoire d'exportations. */ -bool read_pe_image_export_directory(const GPeFormat *, vmpa2t *, image_export_directory *); +bool read_pe_image_export_directory(const GPeFormat *, vmpa2t *, image_export_directory_t *); /* Procède à la lecture d'un répertoire de programme PE. */ -bool read_pe_image_import_descriptor(const GPeFormat *, vmpa2t *, image_import_descriptor *); +bool read_pe_image_import_descriptor(const GPeFormat *, vmpa2t *, image_import_descriptor_t *); -#endif /* _PLUGINS_PE_PE_INT_H */ +#endif /* _PLUGINS_PE_FORMAT_INT_H */ diff --git a/plugins/pe/format.c b/plugins/pe/format.c index 3839063..11bc116 100644 --- a/plugins/pe/format.c +++ b/plugins/pe/format.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * pe.c - support du format Portable Executable + * format.c - support du format Portable Executable * - * Copyright (C) 2010-2017 Cyrille Bagard + * Copyright (C) 2010-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,15 +25,18 @@ #include <assert.h> +#include <string.h> -#include "pe-int.h" -#include "rich.h" -#include "section.h" -#include "symbols.h" +#include "format-int.h" +//#include "rich.h" +//#include "symbols.h" +/* ------------------------- DEFINITION D'UN NOUVEAU FORMAT ------------------------- */ + + /* Initialise la classe des formats d'exécutables ELF. */ static void g_pe_format_class_init(GPeFormatClass *); @@ -46,6 +49,11 @@ static void g_pe_format_dispose(GPeFormat *); /* Procède à la libération totale de la mémoire. */ static void g_pe_format_finalize(GPeFormat *); + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + /* Indique la désignation interne du format. */ static char *g_pe_format_get_key(const GPeFormat *); @@ -53,39 +61,28 @@ static char *g_pe_format_get_key(const GPeFormat *); static char *g_pe_format_get_description(const GPeFormat *); /* Assure l'interprétation d'un format en différé. */ -static bool g_pe_format_analyze(GPeFormat *, wgroup_id_t, GtkStatusStack *); +static bool g_pe_format_analyze(GPeFormat *); /* Informe quant au boutisme utilisé. */ static SourceEndian g_pe_format_get_endianness(const GPeFormat *); /* Indique le type d'architecture visée par le format. */ -static const char *g_pe_format_get_target_machine(const GPeFormat *); +static char *g_pe_format_get_target_machine(const GPeFormat *); -/* Fournit l'adresse principale associée à un format Elf. */ +/* Fournit l'adresse principale associée à un format. */ static bool g_pe_format_get_main_address(GPeFormat *, vmpa2t *); - -#if 0 - /* Etend la définition des portions au sein d'un binaire. */ -static void g_pe_format_refine_portions(GPeFormat *); - -#endif - - - -/* Fournit l'emplacement correspondant à une adresse virtuelle. */ -bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *, virt_t, vmpa2t *); - - -#if 0 +static bool g_pe_format_refine_portions(GPeFormat *); /* Fournit l'emplacement d'une section donnée. */ -static bool g_pe_format_get_section_range_by_name(const GPeFormat *, const char *, mrange_t *); -#endif +static bool g_pe_format_find_section_range_by_name(const GPeFormat *, const char *, mrange_t *); +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UN NOUVEAU FORMAT */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** @@ -116,7 +113,7 @@ bool check_pe_format(const GBinContent *content) if (result) { - G_KNOWN_FORMAT(&format)->content = (GBinContent *)content; + ((GKnownFormat *)&format)->content = (GBinContent *)content; result = read_dos_image_header(&format, &format.dos_header); } @@ -137,7 +134,7 @@ bool check_pe_format(const GBinContent *content) /* Indique le type défini pour un format d'exécutable ELF. */ -G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXE_FORMAT); +G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXECUTABLE_FORMAT); /****************************************************************************** @@ -156,8 +153,8 @@ static void g_pe_format_class_init(GPeFormatClass *klass) { GObjectClass *object; /* Autre version de la classe */ GKnownFormatClass *known; /* Version de format connu */ - GBinFormatClass *fmt; /* Version en format basique */ - GExeFormatClass *exe; /* Version en exécutable */ + GProgramFormatClass *prgm; /* Version en format basique */ + GExecutableFormatClass *exe; /* Version en exécutable */ object = G_OBJECT_CLASS(klass); @@ -170,20 +167,19 @@ static void g_pe_format_class_init(GPeFormatClass *klass) known->get_desc = (known_get_desc_fc)g_pe_format_get_description; known->analyze = (known_analyze_fc)g_pe_format_analyze; - fmt = G_BIN_FORMAT_CLASS(klass); + prgm = G_PROGRAM_FORMAT_CLASS(klass); - fmt->get_endian = (format_get_endian_fc)g_pe_format_get_endianness; + prgm->get_endian = (program_get_endian_fc)g_pe_format_get_endianness; + prgm->find_range_by_name = (find_range_by_name_fc)g_pe_format_find_section_range_by_name; - exe = G_EXE_FORMAT_CLASS(klass); + exe = G_EXECUTABLE_FORMAT_CLASS(klass); exe->get_machine = (get_target_machine_fc)g_pe_format_get_target_machine; exe->get_main_addr = (get_main_addr_fc)g_pe_format_get_main_address; - //exe->refine_portions = (refine_portions_fc)g_pe_format_refine_portions; + exe->refine_portions = (refine_portions_fc)g_pe_format_refine_portions; - //exe->translate_phys = (translate_phys_fc)g_exe_format_translate_offset_into_vmpa_using_portions; - exe->translate_virt = (translate_virt_fc)g_pe_format_translate_address_into_vmpa_using_portions; - - //exe->get_range_by_name = (get_range_by_name_fc)g_pe_format_get_section_range_by_name; + exe->translate_phys = g_executable_format_translate_offset_into_vmpa_with_portions; + exe->translate_virt = g_executable_format_translate_address_into_vmpa_with_portions; } @@ -262,39 +258,41 @@ static void g_pe_format_finalize(GPeFormat *format) * * ******************************************************************************/ -GExeFormat *g_pe_format_new(GBinContent *content) +GPeFormat *g_pe_format_new(GBinContent *content) { GPeFormat *result; /* Structure à retourner */ - if (!check_pe_format(content)) - return NULL; - result = g_object_new(G_TYPE_PE_FORMAT, NULL); - g_known_format_set_content(G_KNOWN_FORMAT(result), content); + if (!g_pe_format_create(result, content)) + g_clear_object(&result); - return G_EXE_FORMAT(result); + return result; } /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * +* Paramètres : format = description du format connu à consulter. * +* content = contenu binaire à parcourir. * * * -* Description : Indique la désignation interne du format. * +* Description : Met en place une nouvelle instance de format PE. * * * -* Retour : Désignation du format. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static char *g_pe_format_get_key(const GPeFormat *format) +bool g_pe_format_create(GPeFormat *format, GBinContent *content) { - char *result; /* Désignation à retourner */ + bool result; /* Bilan à retourner */ - result = strdup("pe"); + result = true;//check_pe_format(content); + + if (result) + result = g_executable_format_create(G_EXECUTABLE_FORMAT(format), content); return result; @@ -303,21 +301,21 @@ static char *g_pe_format_get_key(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * +* Paramètres : format = format en place à consulter. * * * -* Description : Fournit une description humaine du format. * +* Description : Présente l'en-tête MS-DOS du format chargé. * * * -* Retour : Description du format. * +* Retour : Pointeur vers la description principale. * * * * Remarques : - * * * ******************************************************************************/ -static char *g_pe_format_get_description(const GPeFormat *format) +const image_dos_header_t *g_pe_format_get_dos_header(const GPeFormat *format) { - char *result; /* Désignation à retourner */ + const image_dos_header_t *result; /* Informations à retourner */ - result = strdup("Portable Executable"); + result = &format->dos_header; return result; @@ -326,44 +324,21 @@ static char *g_pe_format_get_description(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = format chargé dont l'analyse est lancée. * -* gid = groupe de travail dédié. * -* status = barre de statut à tenir informée. * +* Paramètres : format = format en place à consulter. * * * -* Description : Assure l'interprétation d'un format en différé. * +* Description : Présente l'en-tête NT du format chargé. * * * -* Retour : Bilan de l'opération. * +* Retour : Pointeur vers la description principale. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_pe_format_analyze(GPeFormat *format, wgroup_id_t gid, GtkStatusStack *status) +const image_nt_headers_t *g_pe_format_get_nt_headers(const GPeFormat *format) { - bool result; /* Bilan à retourner */ - GExeFormat *exe; /* Autre version du format */ - vmpa2t section_start; /* Zone de départ des sections */ - - exe = G_EXE_FORMAT(format); - - result = read_dos_image_header(format, &format->dos_header); - if (!result) goto error; - - result = read_pe_nt_header(format, &format->nt_headers, §ion_start); - if (!result) goto error; + const image_nt_headers_t *result; /* Informations à retourner */ - format->sections = read_all_pe_sections(format, §ion_start); - if (format->sections == NULL) goto error; - - extract_pe_rich_header(format); - - result = load_pe_symbols(format, gid, status); - if (!result) goto error; - - result = g_executable_format_complete_loading(exe, gid, status); - if (!result) goto error; - - error: + result = &format->nt_headers; return result; @@ -372,76 +347,73 @@ static bool g_pe_format_analyze(GPeFormat *format, wgroup_id_t gid, GtkStatusSta /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * +* Paramètres : format = format en place à consulter. * * * -* Description : Informe quant au boutisme utilisé. * +* Description : Indique si le format PE est en 32 bits ou en 64 bits. * * * -* Retour : Indicateur de boutisme. * +* Retour : true si le format est en 32 bits, false sinon. * * * * Remarques : - * * * ******************************************************************************/ -static SourceEndian g_pe_format_get_endianness(const GPeFormat *format) +bool g_pe_format_get_is_32b(const GPeFormat *format) { - SourceEndian result; /* Boutisme à retourner */ + bool result; /* Nature à retourner */ - /** - * Sauf exception, le boutisme est généralement petit. - * - * Cf. https://reverseengineering.stackexchange.com/a/17923 - * https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-160#endianness - */ + assert(format->loaded); - result = SRE_LITTLE; + switch (format->nt_headers.optional_header.header_32.magic) + { + case IMAGE_NT_OPTIONAL_HDR32_MAGIC: + result = true; + break; + case IMAGE_NT_OPTIONAL_HDR64_MAGIC: + result = false; + break; + default: + result = true; + assert(false); + break; + } return result; + } /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * +* Paramètres : format = format en place à consulter. * +* count = taille (fixe) du tableau renvoyé. [OUT] * * * -* Description : Indique le type d'architecture visée par le format. * +* Description : Offre un raccourci vers les répertoires du format PE. * * * -* Retour : Identifiant de l'architecture ciblée par le format. * +* Retour : Pointeur vers le tableau des répertoires. * * * * Remarques : - * * * ******************************************************************************/ -static const char *g_pe_format_get_target_machine(const GPeFormat *format) +const image_data_directory_t *g_pe_format_get_directories(const GPeFormat *format, size_t *count) { - const char *result; /* Identifiant à retourner */ + const image_data_directory_t *result; /* Liste à retourner */ - switch (format->nt_headers.file_header.machine) + if (g_pe_format_get_is_32b(format)) { - case IMAGE_FILE_MACHINE_I386: - result = "i386"; - break; + result = format->nt_headers.optional_header.header_32.data_directory; - case IMAGE_FILE_MACHINE_R3000: - case IMAGE_FILE_MACHINE_R4000: - case IMAGE_FILE_MACHINE_R10000: - case IMAGE_FILE_MACHINE_WCEMIPSV2: - case IMAGE_FILE_MACHINE_MIPS16: - case IMAGE_FILE_MACHINE_MIPSFPU: - case IMAGE_FILE_MACHINE_MIPSFPU16: - result = "mips"; - break; + if (count != NULL) + *count = format->nt_headers.optional_header.header_32.number_of_rva_and_sizes; - case IMAGE_FILE_MACHINE_ARM: - case IMAGE_FILE_MACHINE_THUMB: - case IMAGE_FILE_MACHINE_ARMNT: - result = "armv7"; - break; + } + else + { + result = format->nt_headers.optional_header.header_64.data_directory; - case IMAGE_FILE_MACHINE_UNKNOWN: - default: - result = NULL; - break; + if (count != NULL) + *count = format->nt_headers.optional_header.header_64.number_of_rva_and_sizes; } @@ -452,140 +424,174 @@ static const char *g_pe_format_get_target_machine(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* addr = adresse principale trouvée si possible. [OUT] * +* Paramètres : format = format en place à consulter. * +* index = indice du répertoire visé. * * * -* Description : Fournit l'adresse principale associée à un format Elf. * +* Description : Extrait le contenu d'un répertoire du format PE. * * * -* Retour : Bilan des recherches. * +* Retour : Pointeur vers un contenu chargé ou NULL. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_pe_format_get_main_address(GPeFormat *format, vmpa2t *addr) +void *g_pe_format_get_directory(const GPeFormat *format, size_t index) { - bool result; /* Bilan à retourner */ - GBinSymbol *symbol; /* Point d'entrée trouvé */ - GBinFormat *base; /* Version d'instance parente */ - const mrange_t *range; /* Emplacement de ce point */ - - result = false; - symbol = NULL; + void *result; /* Données à retourner */ + size_t max; /* Quantité de répertoires */ + const image_data_directory_t *dir; /* Localisation du répertoire */ + vmpa2t pos; /* Tête de lecture */ + bool status; /* Bilan d'un traitement */ + image_export_directory_t *export; /* Répertoire de type 0 */ + image_import_descriptor_t *imports; /* Répertoire de type 1 */ + size_t imported_count; /* Quantité de DLL requises */ - base = G_BIN_FORMAT(format); + result = NULL; - if (g_binary_format_find_symbol_by_label(base, "main", &symbol)) - goto done; + dir = g_pe_format_get_directories(format, &max); - if (g_binary_format_find_symbol_by_label(base, "_start", &symbol)) - goto done; + if (index >= max) + goto exit; - if (g_binary_format_find_symbol_by_label(base, "entry_point", &symbol)) - goto done; + dir += index; - done: + status = g_executable_format_translate_address_into_vmpa(G_EXECUTABLE_FORMAT(format), + dir->virtual_address, &pos); + if (!status) goto exit; - if (symbol != NULL) + switch (index) { - result = true; + case IMAGE_DIRECTORY_ENTRY_EXPORT: + + export = malloc(sizeof(image_export_directory_t)); + + status = read_pe_image_export_directory(format, &pos, export); + + if (!status) + { + free(export); + goto exit; + } - range = g_binary_symbol_get_range(symbol); + result = export; + break; - copy_vmpa(addr, get_mrange_addr(range)); + case IMAGE_DIRECTORY_ENTRY_IMPORT: - g_object_unref(G_OBJECT(symbol)); + imports = NULL; + imported_count = 0; + + do + { + imports = realloc(imports, ++imported_count * sizeof(image_import_descriptor_t)); + + status = read_pe_image_import_descriptor(format, &pos, imports + (imported_count - 1)); + + if (!status) + { + free(imports); + goto exit; + } + + } + while (imports[imported_count - 1].original_first_thunk != 0); + + result = imports; + break; } + exit: + return result; } -#if 0 - /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * +* Paramètres : format = format en place à consulter. * +* count = taille (fixe) du tableau renvoyé. [OUT] * * * -* Description : Etend la définition des portions au sein d'un binaire. * +* Description : Offre un raccourci vers les sections du format PE. * * * -* Retour : - * +* Retour : Pointeur vers la liste des sections. * * * * Remarques : - * * * ******************************************************************************/ -static void g_pe_format_refine_portions(GPeFormat *format) +const image_section_header_t *g_pe_format_get_sections(const GPeFormat *format, size_t *count) { + const image_section_header_t *result; /* Liste à retourner */ + + if (count != NULL) + *count = format->nt_headers.file_header.number_of_sections; + + result = format->sections; + + return result; } -#endif +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * -* addr = adresse virtuelle à retrouver. * -* pos = position correspondante. [OUT] * * * -* Description : Fournit l'emplacement correspondant à une adresse virtuelle. * +* Description : Indique la désignation interne du format. * * * -* Retour : Bilan de l'opération. * +* Retour : Désignation du format. * * * * Remarques : - * * * ******************************************************************************/ -bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *format, virt_t addr, vmpa2t *pos) +static char *g_pe_format_get_key(const GPeFormat *format) { - bool result; /* Bilan à retourner */ - uint16_t i; /* Boucle de parcours */ - const image_section_header *section; /* Section à consulter */ - phys_t diff; /* Décallage à appliquer */ - - result = false; - - for (i = 0; i < format->nt_headers.file_header.number_of_sections && !result; i++) - { - section = &format->sections[i]; + char *result; /* Désignation à retourner */ - if (addr < section->virtual_address) - continue; + result = strdup("pe"); - if (addr >= (section->virtual_address + section->size_of_raw_data)) - continue; + return result; - diff = addr - section->virtual_address; +} - init_vmpa(pos, section->pointer_to_raw_data + diff, addr); - result = true; +/****************************************************************************** +* * +* Paramètres : format = description de l'exécutable à consulter. * +* * +* Description : Fournit une description humaine du format. * +* * +* Retour : Description du format. * +* * +* Remarques : - * +* * +******************************************************************************/ - } +static char *g_pe_format_get_description(const GPeFormat *format) +{ + char *result; /* Désignation à retourner */ - //printf(" // trans // %x -> %x (valid? %d)\n", (unsigned int)addr, (unsigned int)pos->physical, result); + result = strdup("Portable Executable"); return result; } - - -#if 0 - - /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* name = nom de la section recherchée. * -* range = emplacement en mémoire à renseigner. [OUT] * +* Paramètres : format = format chargé dont l'analyse est lancée. * * * -* Description : Fournit l'emplacement d'une section donnée. * +* Description : Assure l'interprétation d'un format en différé. * * * * Retour : Bilan de l'opération. * * * @@ -593,51 +599,82 @@ bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *format, v * * ******************************************************************************/ -static bool g_pe_format_get_section_range_by_name(const GPeFormat *format, const char *name, mrange_t *range) +static bool g_pe_format_analyze(GPeFormat *format) { bool result; /* Bilan à retourner */ - phys_t offset; /* Position physique de section*/ - phys_t size; /* Taille de la section trouvée*/ - virt_t address; /* Adresse virtuelle de section*/ - vmpa2t tmp; /* Adresse à initialiser */ + vmpa2t start; /* Zone de départ des sections */ + uint16_t count; /* Quantité de sections */ + uint16_t i; /* Boucle de parcours */ - result = find_elf_section_content_by_name(format, name, &offset, &size, &address); + result = read_dos_image_header(format, &format->dos_header); + if (!result) goto error; - if (result) + result = read_pe_nt_header(format, &format->nt_headers, &start); + if (!result) goto error; + + /* Chargement des définitions des sections déclarées */ + + copy_vmpa(&format->sections_start, &start); + + count = format->nt_headers.file_header.number_of_sections; + + format->sections = malloc(count * sizeof(image_section_header_t)); + + for (i = 0; i < count; i++) { - init_vmpa(&tmp, offset, address); - init_mrange(range, &tmp, size); + result = read_pe_image_section_header(format, &start, &format->sections[i]); + if (!result) goto error; } - return result; + /* Passage de relais */ -} -#endif + if (result) + result = G_KNOWN_FORMAT_CLASS(g_pe_format_parent_class)->analyze(G_KNOWN_FORMAT(format)); + + + + +#if 0 + extract_pe_rich_header(format); + result = load_pe_symbols(format, gid, status); + if (!result) goto error; + +#endif + error: + return result; +} /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * +* Paramètres : format = informations chargées à consulter. * * * -* Description : Présente l'en-tête MS-DOS du format chargé. * +* Description : Informe quant au boutisme utilisé. * * * -* Retour : Pointeur vers la description principale. * +* Retour : Indicateur de boutisme. * * * * Remarques : - * * * ******************************************************************************/ -const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *format) +static SourceEndian g_pe_format_get_endianness(const GPeFormat *format) { - const image_dos_header *result; /* Informations à retourner */ + SourceEndian result; /* Boutisme à retourner */ - result = &format->dos_header; + /** + * Sauf exception, le boutisme est généralement petit. + * + * Cf. https://reverseengineering.stackexchange.com/a/17923 + * https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-160#endianness + */ + + result = SRE_LITTLE; return result; @@ -646,21 +683,43 @@ const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * +* Paramètres : format = description de l'exécutable à consulter. * +* name = nom de la section recherchée. * +* range = emplacement en mémoire à renseigner. [OUT] * * * -* Description : Présente l'en-tête NT du format chargé. * +* Description : Fournit l'emplacement d'une section donnée. * * * -* Retour : Pointeur vers la description principale. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *format) +static bool g_pe_format_find_section_range_by_name(const GPeFormat *format, const char *name, mrange_t *range) { - const image_nt_headers *result; /* Informations à retourner */ + bool result; /* Bilan à retourner */ + uint16_t i; /* Boucle de parcours */ + image_section_header_t *section; /* Section à traiter */ + char tmp[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de la section */ + vmpa2t addr; /* Emplacement dans le binaire */ - result = &format->nt_headers; + result = false; + + for (i = 0, section = format->sections; i < format->nt_headers.file_header.number_of_sections; i++, section++) + { + memcpy(tmp, section->name, IMAGE_SIZEOF_SHORT_NAME); + tmp[IMAGE_SIZEOF_SHORT_NAME] = '\0'; + + if (strcmp(name, tmp) != 0) + continue; + + init_vmpa(&addr, section->pointer_to_raw_data, section->virtual_address); + init_mrange(range, &addr, section->size_of_raw_data); + + result = true; + break; + + } return result; @@ -669,75 +728,78 @@ const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * +* Paramètres : format = informations chargées à consulter. * * * -* Description : Indique si le format PE est en 32 bits ou en 64 bits. * +* Description : Indique le type d'architecture visée par le format. * * * -* Retour : true si le format est en 32 bits, false sinon. * +* Retour : Identifiant de l'architecture ciblée par le format. * * * * Remarques : - * * * ******************************************************************************/ -bool g_pe_format_get_is_32b(const GPeFormat *format) +static char *g_pe_format_get_target_machine(const GPeFormat *format) { - bool result; /* Nature à retourner */ - - assert(format->loaded); + char *result; /* Identifiant à retourner */ - switch (format->nt_headers.optional_header.header_32.magic) + switch (format->nt_headers.file_header.machine) { - case IMAGE_NT_OPTIONAL_HDR32_MAGIC: - result = true; + case IMAGE_FILE_MACHINE_I386: + result = strdup("i386"); break; - case IMAGE_NT_OPTIONAL_HDR64_MAGIC: - result = false; + + case IMAGE_FILE_MACHINE_R3000: + case IMAGE_FILE_MACHINE_R4000: + case IMAGE_FILE_MACHINE_R10000: + case IMAGE_FILE_MACHINE_WCEMIPSV2: + case IMAGE_FILE_MACHINE_MIPS16: + case IMAGE_FILE_MACHINE_MIPSFPU: + case IMAGE_FILE_MACHINE_MIPSFPU16: + result = strdup("mips"); break; + + case IMAGE_FILE_MACHINE_ARM: + case IMAGE_FILE_MACHINE_THUMB: + case IMAGE_FILE_MACHINE_ARMNT: + result = strdup("armv7"); + break; + + case IMAGE_FILE_MACHINE_UNKNOWN: default: - result = true; - assert(false); + result = NULL; break; + } return result; - } /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * -* count = taille (fixe) du tableau renvoyé. [OUT] * +* Paramètres : format = description de l'exécutable à consulter. * +* addr = adresse principale trouvée si possible. [OUT] * * * -* Description : Offre un raccourci vers les répertoires du format PE. * +* Description : Fournit l'adresse principale associée à un format. * * * -* Retour : Pointeur vers le tableau des répertoires. * +* Retour : Bilan des recherches. * * * * Remarques : - * * * ******************************************************************************/ -const image_data_directory *g_pe_format_get_directories(const GPeFormat *format, size_t *count) +static bool g_pe_format_get_main_address(GPeFormat *format, vmpa2t *addr) { - const image_data_directory *result; /* Liste à retourner */ + bool result; /* Bilan à retourner */ + virt_t ep; /* Point d'entrée */ if (g_pe_format_get_is_32b(format)) - { - result = format->nt_headers.optional_header.header_32.data_directory; - - if (count != NULL) - *count = format->nt_headers.optional_header.header_32.number_of_rva_and_sizes; - - } + ep = format->nt_headers.optional_header.header_32.address_of_entry_point; else - { - result = format->nt_headers.optional_header.header_64.data_directory; - - if (count != NULL) - *count = format->nt_headers.optional_header.header_64.number_of_rva_and_sizes; + ep = format->nt_headers.optional_header.header_64.address_of_entry_point; - } + result = g_executable_format_translate_address_into_vmpa(G_EXECUTABLE_FORMAT(format), ep, addr); return result; @@ -746,83 +808,75 @@ const image_data_directory *g_pe_format_get_directories(const GPeFormat *format, /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * -* index = indice du répertoire visé. * +* Paramètres : format = informations chargées à consulter. * * * -* Description : Extrait le contenu d'un répertoire du format PE. * +* Description : Etend la définition des portions au sein d'un binaire. * * * -* Retour : Pointeur vers un contenu chargé ou NULL. * +* Retour : Bilan des définitions de portions. * * * * Remarques : - * * * ******************************************************************************/ -void *g_pe_format_get_directory(const GPeFormat *format, size_t index) +static bool g_pe_format_refine_portions(GPeFormat *format) { - void *result; /* Données à retourner */ - size_t max; /* Quantité de répertoires */ - const image_data_directory *dir; /* Localisation du répertoire */ - vmpa2t pos; /* Tête de lecture */ - bool status; /* Bilan d'un traitement */ - image_export_directory *export; /* Répertoire de type 0 */ - image_import_descriptor *imports; /* Répertoire de type 1 */ - size_t imported_count; /* Quantité de DLL requises */ + bool result; /* Bilan à retourner */ + vmpa2t origin; /* Origine d'une définition */ + uint16_t i; /* Boucle de parcours */ + image_section_header_t *section; /* Section à traiter */ + vmpa2t addr; /* Emplacement dans le binaire */ + GBinaryPortion *portion; /* Nouvelle portion de binaire */ + char name[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de la section */ + PortionAccessRights rights; /* Droits d'accès à la section */ - result = NULL; + result = true; - dir = g_pe_format_get_directories(format, &max); + copy_vmpa(&origin, &format->sections_start); - if (index >= max) - goto exit; + for (i = 0, section = format->sections; i < format->nt_headers.file_header.number_of_sections; i++, section++) + { + if (section->pointer_to_raw_data == 0) + continue; - dir += index; + /* Emplacement */ - status = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), dir->virtual_address, &pos); - if (!status) goto exit; + init_vmpa(&addr, section->pointer_to_raw_data, section->virtual_address); - switch (index) - { - case IMAGE_DIRECTORY_ENTRY_EXPORT: + portion = g_binary_portion_new(&addr, section->size_of_raw_data); - export = malloc(sizeof(image_export_directory)); + /* Nom */ - status = read_pe_image_export_directory(format, &pos, export); + memcpy(name, section->name, IMAGE_SIZEOF_SHORT_NAME); + name[IMAGE_SIZEOF_SHORT_NAME] = '\0'; - if (!status) - { - free(export); - goto exit; - } + g_binary_portion_set_desc(portion, name); - result = export; - break; + /* Droits d'accès */ - case IMAGE_DIRECTORY_ENTRY_IMPORT: + rights = PAC_NONE; - imports = NULL; - imported_count = 0; + if (section->characteristics & IMAGE_SCN_MEM_EXECUTE) + rights |= PAC_EXEC; - do - { - imports = realloc(imports, ++imported_count * sizeof(image_import_descriptor)); + if (section->characteristics & IMAGE_SCN_MEM_READ) + rights |= PAC_READ; - status = read_pe_image_import_descriptor(format, &pos, imports + (imported_count - 1)); + if (section->characteristics & IMAGE_SCN_MEM_WRITE) + rights |= PAC_WRITE; - if (!status) - { - free(imports); - goto exit; - } + g_binary_portion_set_rights(portion, rights); - } - while (imports[imported_count - 1].original_first_thunk != 0); + /* Inclusion finale */ - result = imports; - break; + result = g_executable_format_include_portion(G_EXECUTABLE_FORMAT(format), portion, &origin); - } + unref_object(portion); - exit: + if (!result) break; + + advance_vmpa(&origin, sizeof(image_section_header_t)); + + } return result; diff --git a/plugins/pe/format.h b/plugins/pe/format.h index a48e04d..829f4de 100644 --- a/plugins/pe/format.h +++ b/plugins/pe/format.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * pe.h - prototypes pour le support du format Portable Executable + * format.h - prototypes pour le support du format Portable Executable * - * Copyright (C) 2010-2017 Cyrille Bagard + * Copyright (C) 2010-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,31 +25,20 @@ #define _PLUGINS_PE_FORMAT_H -#include <glib-object.h> #include <stdbool.h> #include <analysis/content.h> -#include <format/executable.h> +#include <glibext/helpers.h> -#include "pe_def.h" +#include "pe-def.h" -#define G_TYPE_PE_FORMAT g_pe_format_get_type() -#define G_PE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PE_FORMAT, GPeFormat)) -#define G_IS_PE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PE_FORMAT)) -#define G_PE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PE_FORMAT, GPeFormatClass)) -#define G_IS_PE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PE_FORMAT)) -#define G_PE_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PE_FORMAT, GPeFormatClass)) +#define G_TYPE_PE_FORMAT (g_pe_format_get_type()) - -/* Format d'exécutable PE (instance) */ -typedef struct _GPeFormat GPeFormat; - -/* Format d'exécutable PE (classe) */ -typedef struct _GPeFormatClass GPeFormatClass; +DECLARE_GTYPE(GPeFormat, g_pe_format, G, PE_FORMAT); /* Valide un contenu comme étant un format PE. */ @@ -59,23 +48,26 @@ bool check_pe_format(const GBinContent *); GType g_pe_format_get_type(void); /* Prend en charge un nouveau format PE. */ -GExeFormat *g_pe_format_new(GBinContent *); +GPeFormat *g_pe_format_new(GBinContent *); /* Présente l'en-tête MS-DOS du format chargé. */ -const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *); +const image_dos_header_t *g_pe_format_get_dos_header(const GPeFormat *); /* Présente l'en-tête NT du format chargé. */ -const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *); +const image_nt_headers_t *g_pe_format_get_nt_headers(const GPeFormat *); /* Indique si le format PE est en 32 bits ou en 64 bits. */ bool g_pe_format_get_is_32b(const GPeFormat *); /* Offre un raccourci vers les répertoires du format PE. */ -const image_data_directory *g_pe_format_get_directories(const GPeFormat *, size_t *); +const image_data_directory_t *g_pe_format_get_directories(const GPeFormat *, size_t *); /* Extrait le contenu d'un répertoire du format PE. */ void *g_pe_format_get_directory(const GPeFormat *, size_t); +/* Offre un raccourci vers les sections du format PE. */ +const image_section_header_t *g_pe_format_get_sections(const GPeFormat *, size_t *); + #endif /* _PLUGINS_PE_FORMAT_H */ diff --git a/plugins/pe/pe_def.h b/plugins/pe/pe-def.h index 62b4607..4812897 100644 --- a/plugins/pe/pe_def.h +++ b/plugins/pe/pe-def.h @@ -31,6 +31,7 @@ /** * Références : * + * - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format * - https://fr.wikipedia.org/wiki/Portable_Executable#En-tête_MZ_sous_MS-DOS * - https://www.nirsoft.net/kernel_struct/vista/IMAGE_DOS_HEADER.html * @@ -42,7 +43,7 @@ /* En-tête DOS */ -typedef struct _image_dos_header +typedef struct _image_dos_header_t { uint16_t e_magic; /* Numéro magique */ uint16_t e_cblp; /* Octets de la dernière page */ @@ -64,7 +65,7 @@ typedef struct _image_dos_header uint16_t e_res2[10]; /* Mots réservés */ uint32_t e_lfanew; /* Décalage de bon en-tête */ -} image_dos_header; +} image_dos_header_t; /* Archtectures supportées */ @@ -123,7 +124,7 @@ typedef struct _image_dos_header #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 /* Octets inv. ; obsolète */ /* Première en-tête du "vrai" format */ -typedef struct _image_file_header +typedef struct _image_file_header_t { uint16_t machine; /* Type de machine visée */ uint16_t number_of_sections; /* Nombre de sections */ @@ -133,7 +134,7 @@ typedef struct _image_file_header uint16_t size_of_optional_header; /* Taille de l'en-tête n°2 */ uint16_t characteristics; /* Propriétés de l'image */ -} image_file_header; +} image_file_header_t; @@ -148,12 +149,12 @@ typedef struct _image_file_header */ /* Zone de données Windows */ -typedef struct _image_data_directory +typedef struct _image_data_directory_t { uint32_t virtual_address; /* Adresse de la table */ uint32_t size; /* Taille de la table */ -} image_data_directory; +} image_data_directory_t; // Directory Entries #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory @@ -182,7 +183,7 @@ typedef struct _image_data_directory /* Seconde en-tête, optionnelle */ -typedef struct _image_optional_header_32 +typedef struct _image_optional_header_32_t { uint16_t magic; /* Type de binaire manipulé */ uint8_t major_linker_version; /* Version majeure du linker */ @@ -214,11 +215,11 @@ typedef struct _image_optional_header_32 uint32_t size_of_heap_commit; /* Taille de tas au démarrage */ uint32_t loader_flags; /* Champ obslète */ uint32_t number_of_rva_and_sizes; /* Nombre d'entrées suivantes */ - image_data_directory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + image_data_directory_t data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -} image_optional_header_32; +} image_optional_header_32_t; -typedef struct _image_optional_header_64 +typedef struct _image_optional_header_64_t { uint16_t magic; /* Type de binaire manipulé */ @@ -250,16 +251,16 @@ typedef struct _image_optional_header_64 uint64_t size_of_heap_commit; /* Taille de tas au démarrage */ uint32_t loader_flags; /* Champ obslète */ uint32_t number_of_rva_and_sizes; /* Nombre d'entrées suivantes */ - image_data_directory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + image_data_directory_t data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -} image_optional_header_64; +} image_optional_header_64_t; -typedef union _image_optional_header +typedef union _image_optional_header_t { - image_optional_header_32 header_32; /* Version 32 bits */ - image_optional_header_64 header_64; /* Version 64 bits */ + image_optional_header_32_t header_32; /* Version 32 bits */ + image_optional_header_64_t header_64; /* Version 64 bits */ -} image_optional_header; +} image_optional_header_t; @@ -302,13 +303,13 @@ typedef union _image_optional_header /* Résumé global */ -typedef struct _image_nt_headers +typedef struct _image_nt_headers_t { uint32_t signature; /* Numéro magique */ - image_file_header file_header; /* En-tête n°1 */ - image_optional_header optional_header; /* En-tête n°2 */ + image_file_header_t file_header; /* En-tête n°1 */ + image_optional_header_t optional_header;/* En-tête n°2 */ -} image_nt_headers; +} image_nt_headers_t; @@ -322,7 +323,7 @@ typedef struct _image_nt_headers #define IMAGE_SIZEOF_SHORT_NAME 8 /* Description d'une section */ -typedef struct _image_section_header +typedef struct _image_section_header_t { char name[IMAGE_SIZEOF_SHORT_NAME]; /* Nom de la section */ @@ -331,7 +332,7 @@ typedef struct _image_section_header uint32_t physical_address; /* Adresse physique */ uint32_t virtual_size; /* Taille en mémoire */ - } misc; + }; uint32_t virtual_address; /* Adresse en mémoire */ uint32_t size_of_raw_data; /* Taille de données définies */ @@ -342,7 +343,7 @@ typedef struct _image_section_header uint16_t number_of_line_numbers; /* Quantité de numéros de ligne*/ uint32_t characteristics; /* Caractéristiques */ -} image_section_header; +} image_section_header_t; /* Détails des caractéristiques d'une image (champ 'characteristics') */ #define IMAGE_SCN_UNKNOWN_0 0x00000000 /* Réservé */ @@ -400,7 +401,7 @@ typedef struct _image_section_header */ /* Répertoire des importations */ -typedef struct _image_export_directory +typedef struct _image_export_directory_t { uint32_t characteristics; /* Zéro !? */ uint32_t time_date_stamp; /* Date de création du fichier */ @@ -414,7 +415,7 @@ typedef struct _image_export_directory uint32_t address_of_names; /* Liste de RVA de noms */ uint32_t address_of_name_ordinals; /* Liste de RVA d'ordinaux */ -} image_export_directory; +} image_export_directory_t; /** @@ -427,7 +428,7 @@ typedef struct _image_export_directory */ /* Point de départ de la chaîne des importations */ -typedef struct _image_import_descriptor +typedef struct _image_import_descriptor_t { uint32_t original_first_thunk; uint32_t time_date_stamp; @@ -435,7 +436,7 @@ typedef struct _image_import_descriptor uint32_t module_name; uint32_t first_thunk; -} image_import_descriptor; +} image_import_descriptor_t; diff --git a/plugins/pe/python/Makefile.am b/plugins/pe/python/Makefile.am index 5949821..4a70769 100644 --- a/plugins/pe/python/Makefile.am +++ b/plugins/pe/python/Makefile.am @@ -1,14 +1,16 @@ noinst_LTLIBRARIES = libpepython.la +# libpepython_la_SOURCES = \ +# constants.h constants.c \ +# routine.h routine.c + libpepython_la_SOURCES = \ - constants.h constants.c \ format.h format.c \ module.h module.c \ - routine.h routine.c \ translate.h translate.c -libpepython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpepython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pe/python/constants.c b/plugins/pe/python/constants.c index 9b4942d..bb10a7e 100644 --- a/plugins/pe/python/constants.c +++ b/plugins/pe/python/constants.c @@ -29,7 +29,7 @@ #include "../pe_def.h" -#include "../routine.h" +//#include "../routine.h" @@ -101,7 +101,7 @@ bool define_python_pe_format_constants(PyTypeObject *type) * Remarques : - * * * ******************************************************************************/ - +#if 0 bool define_python_pe_exported_routine_constants(PyTypeObject *type) { bool result; /* Bilan à retourner */ @@ -141,3 +141,4 @@ bool define_python_pe_exported_routine_constants(PyTypeObject *type) return result; } +#endif diff --git a/plugins/pe/python/constants.h b/plugins/pe/python/constants.h index 25b0adb..fe4293c 100644 --- a/plugins/pe/python/constants.h +++ b/plugins/pe/python/constants.h @@ -35,7 +35,7 @@ bool define_python_pe_format_constants(PyTypeObject *); /* Définit les constantes pour les routines du format PE. */ -bool define_python_pe_exported_routine_constants(PyTypeObject *); +//bool define_python_pe_exported_routine_constants(PyTypeObject *); diff --git a/plugins/pe/python/format.c b/plugins/pe/python/format.c index 4bbb99a..71d3305 100644 --- a/plugins/pe/python/format.c +++ b/plugins/pe/python/format.c @@ -28,25 +28,22 @@ #include <pygobject.h> -#include <format/known.h> -#include <plugins/dt.h> #include <plugins/pychrysalide/helpers.h> #include <plugins/pychrysalide/analysis/content.h> #include <plugins/pychrysalide/format/executable.h> -#include "constants.h" +//#include "constants.h" #include "translate.h" -#include "../format.h" -#include "../rich.h" +#include "../format-int.h" +//#include "../rich.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_pe_format_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(pe_format, G_TYPE_PE_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_pe_format_init(PyObject *, PyObject *, PyObject *); @@ -59,9 +56,15 @@ static int py_pe_format_init(PyObject *, PyObject *, PyObject *); /* Présente l'en-tête MS-DOS du format chargé. */ static PyObject *py_pe_format_get_dos_header(PyObject *, void *); +/* Présente l'en-tête NT du format chargé. */ +static PyObject *py_pe_format_get_nt_headers(PyObject *, void *); + /* Offre un raccourci vers les répertoires du format PE. */ static PyObject *py_pe_format_get_directories(PyObject *, void *); +/* Offre un raccourci vers les sections du format PE. */ +static PyObject *py_pe_format_get_sections(PyObject *, void *); + /* Présente l'en-tête enrichi du format chargé. */ static PyObject *py_pe_format_get_rich_header(PyObject *, void *); @@ -80,66 +83,6 @@ static PyObject *py_pe_format_get_comp_ids(PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_pe_format_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_pe_format_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_PE_FORMAT, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -183,7 +126,8 @@ static int py_pe_format_init(PyObject *self, PyObject *args, PyObject *kwds) format = G_PE_FORMAT(pygobject_get(self)); - g_known_format_set_content(G_KNOWN_FORMAT(format), content); + if (!g_pe_format_create(format, content)) + return -1; return 0; @@ -223,7 +167,6 @@ static PyObject *py_pe_format_get_dos_header(PyObject *self, void *closure) "\n" \ "The provided information is composed of the following" \ " properties :\n" \ - "\n" \ "* e_magic;\n" \ "* e_cblp;\n" \ "* e_cp;\n" \ @@ -259,6 +202,52 @@ static PyObject *py_pe_format_get_dos_header(PyObject *self, void *closure) * Paramètres : self = format en place à consulter. * * closure = non utilisé ici. * * * +* Description : Présente l'en-tête NT du format chargé. * +* * +* Retour : Structure Python créée pour l'occasion. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_pe_format_get_nt_headers(PyObject *self, void *closure) +{ + PyObject *result; /* Trouvaille à retourner */ + GPeFormat *format; /* Version GLib du format */ + +#define PE_FORMAT_NT_HEADERS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + nt_headers, py_pe_format, \ + "NT headers of the file format.\n" \ + "\n" \ + "This property is a pychrysalide.StructObject instance." \ + "\n" \ + "The provided information is composed of the following" \ + " properties :\n" \ + "* signature;\n" \ + "* file_header;\n" \ + "* optional_header.\n" \ + "\n" \ + "The last two fields are pychrysalide.StructObject" \ + " which contain more fields. These fields can be" \ + " enumerated with the keys() method (for instance:" \ + " *mype.nt_headers.file_header.keys()*).\n" \ +) + + format = G_PE_FORMAT(pygobject_get(self)); + + result = translate_pe_nt_headers_to_python(format, g_pe_format_get_nt_headers(format)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = format en place à consulter. * +* closure = non utilisé ici. * +* * * Description : Offre un raccourci vers les répertoires du format PE. * * * * Retour : Structure Python créée pour l'occasion. * @@ -272,7 +261,7 @@ static PyObject *py_pe_format_get_directories(PyObject *self, void *closure) PyObject *result; /* Trouvaille à retourner */ GPeFormat *format; /* Version GLib du format */ size_t count; /* Quantité de répertoires */ - const image_data_directory *directories; /* Répertoires à exporter */ + const image_data_directory_t *directories; /* Répertoires à exporter */ size_t i; /* Boucle de parcours */ PyObject *item; /* Elément de tableau */ int ret; /* Bilan d'une mise en place */ @@ -323,6 +312,76 @@ static PyObject *py_pe_format_get_directories(PyObject *self, void *closure) * Paramètres : self = format en place à consulter. * * closure = non utilisé ici. * * * +* Description : Offre un raccourci vers les sections du format PE. * +* * +* Retour : Structure Python créée pour l'occasion. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_pe_format_get_sections(PyObject *self, void *closure) +{ + PyObject *result; /* Trouvaille à retourner */ + GPeFormat *format; /* Version GLib du format */ + size_t count; /* Quantité de répertoires */ + const image_section_header_t *sections; /* Sections à exporter */ + size_t i; /* Boucle de parcours */ + PyObject *item; /* Elément de tableau */ + int ret; /* Bilan d'une mise en place */ + +#define PE_FORMAT_SECTIONS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + sections, py_pe_format, \ + "Shortcut to the definitions of all PE format sections.\n" \ + "\n" \ + "This property is a pychrysalide.StructObject instance.\n" \ + "\n" \ + "Each returned item is composed of the following properties :\n"\ + "\n" \ + "* name;\n" \ + "* misc.virtual_size;\n" \ + "* virtual_address;\n" \ + "* size_of_raw_data;\n" \ + "* pointer_to_raw_data;\n" \ + "* pointer_to_relocations;\n" \ + "* pointer_to_line_numbers;\n" \ + "* number_of_relocations;\n" \ + "* number_of_line_numbers;\n" \ + "* characteristics." \ +) + + format = G_PE_FORMAT(pygobject_get(self)); + + sections = g_pe_format_get_sections(format, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + { + item = translate_pe_section_header_to_python(format, sections + i); + + ret = PyTuple_SetItem(result, i, item); + + if (ret != 0) + { + Py_DECREF(result); + result = NULL; + break; + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = format en place à consulter. * +* closure = non utilisé ici. * +* * * Description : Présente l'en-tête enrichi du format chargé. * * * * Retour : Tableau de valeurs brutes d'information. * @@ -487,7 +546,9 @@ PyTypeObject *get_python_pe_format_type(void) static PyGetSetDef py_pe_format_getseters[] = { PE_FORMAT_DOS_HEADER_ATTRIB, + PE_FORMAT_NT_HEADERS_ATTRIB, PE_FORMAT_DIRECTORIES_ATTRIB, + PE_FORMAT_SECTIONS_ATTRIB, PE_FORMAT_RICH_HEADER_ATTRIB, PE_FORMAT_RICH_HEADER_CHECKSUM_ATTRIB, PE_FORMAT_COMP_IDS_ATTRIB, @@ -545,8 +606,8 @@ bool register_python_pe_format(PyObject *module) if (!register_class_for_pygobject(dict, G_TYPE_PE_FORMAT, type)) return false; - if (!define_python_pe_format_constants(type)) - return false; + //if (!define_python_pe_format_constants(type)) + // return false; return true; diff --git a/plugins/pe/python/module.c b/plugins/pe/python/module.c index 93b1337..ce0c8d7 100644 --- a/plugins/pe/python/module.c +++ b/plugins/pe/python/module.c @@ -33,7 +33,7 @@ #include "format.h" -#include "routine.h" +//#include "routine.h" @@ -83,8 +83,8 @@ bool add_format_pe_module_to_python_module(void) result = (module != NULL); if (result) result = register_python_pe_format(module); - if (result) result = register_python_pe_exported_routine(module); - if (result) result = register_python_pe_imported_routine(module); + //if (result) result = register_python_pe_exported_routine(module); + //if (result) result = register_python_pe_imported_routine(module); assert(result); diff --git a/plugins/pe/python/translate.c b/plugins/pe/python/translate.c index c01a337..1b4b3ce 100644 --- a/plugins/pe/python/translate.c +++ b/plugins/pe/python/translate.c @@ -45,7 +45,7 @@ * * ******************************************************************************/ -PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_header *header) +PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_header_t *header) { PyObject *result; /* Construction à retourner */ PyTypeObject *base; /* Modèle d'objet à créer */ @@ -64,6 +64,7 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h { \ attrib = PyLong_FromUnsignedLongLong(header->e_ ## _f); \ ret = PyDict_SetItemString(result, "e_" #_f, attrib); \ + Py_DECREF(attrib); \ if (ret != 0) goto failed; \ } \ while (0); @@ -79,8 +80,13 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h ret = PyTuple_SetItem(attrib, i, item); \ if (ret != 0) break; \ } \ - if (ret != 0) goto failed; \ + if (i < _n) \ + { \ + Py_DECREF(attrib); \ + goto failed; \ + } \ ret = PyDict_SetItemString(result, "e_" #_f, attrib); \ + Py_DECREF(attrib); \ if (ret != 0) goto failed; \ } \ while (0); @@ -119,6 +125,215 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h /****************************************************************************** * * * Paramètres : format = format PE chargé sur lequel s'appuyer. * +* header = en-tête NT à décrire en Python. * +* * +* Description : Traduit un en-tête PE en Python. * +* * +* Retour : Structure mise en place ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *translate_pe_nt_headers_to_python(GPeFormat *format, const image_nt_headers_t *header) +{ + PyObject *result; /* Construction à retourner */ + PyTypeObject *base; /* Modèle d'objet à créer */ + PyObject *attrib; /* Attribut à constituer */ + int ret; /* Bilan d'une mise en place */ + PyObject *sub; /* Sous-construction #1 */ + bool is_32b; /* Format en version 32 bits ? */ + const image_data_directory_t *directories; /* Répertoires à charger */ + uint32_t number_of_rva_and_sizes; /* Quantité de ces répertoires */ + uint32_t i; /* Boucle de parcours */ + PyObject *dirs; /* Répertoires de données */ + PyObject *subsub; /* Sous-construction #2 */ + + base = get_python_py_struct_type(); + + result = PyObject_CallFunction((PyObject *)base, NULL); + assert(result != NULL); + +#define TRANSLATE_IMAGE_NT_HEADERS_FIELD(_f) \ + do \ + { \ + attrib = PyLong_FromUnsignedLongLong(header->_f); \ + ret = PyDict_SetItemString(result, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) goto failed; \ + } \ + while (0); + + TRANSLATE_IMAGE_NT_HEADERS_FIELD(signature); + + /* Partie file_header */ + + sub = PyObject_CallFunction((PyObject *)base, NULL); + assert(sub != NULL); + +#define TRANSLATE_IMAGE_FILE_HEADER_FIELD(_f) \ + do \ + { \ + attrib = PyLong_FromUnsignedLongLong(header->file_header._f); \ + ret = PyDict_SetItemString(sub, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) \ + { \ + Py_DECREF(sub); \ + goto failed; \ + } \ + } \ + while (0); + + TRANSLATE_IMAGE_FILE_HEADER_FIELD(machine); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(number_of_sections); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(time_date_stamp); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(pointer_to_symbol_table); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(number_of_symbols); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(size_of_optional_header); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(characteristics); + + ret = PyDict_SetItemString(result, "file_header", sub); + Py_DECREF(sub); + if (ret != 0) goto failed; + + /* Partie optional_header */ + + sub = PyObject_CallFunction((PyObject *)base, NULL); + assert(sub != NULL); + + is_32b = g_pe_format_get_is_32b(format); + +#define TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(_f) \ + do \ + { \ + if (is_32b) \ + attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_32._f); \ + else \ + attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_64._f); \ + ret = PyDict_SetItemString(sub, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) \ + { \ + Py_DECREF(sub); \ + goto failed; \ + } \ + } \ + while (0); + +#define TRANSLATE_IMAGE_OPTIONAL_HEADER_32B_FIELD(_f) \ + do \ + { \ + attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_32._f); \ + ret = PyDict_SetItemString(sub, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) \ + { \ + Py_DECREF(sub); \ + goto failed; \ + } \ + } \ + while (0); + + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(magic); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_linker_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_linker_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_code); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_initialized_data); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_uninitialized_data); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(address_of_entry_point); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(base_of_code); + if (is_32b) TRANSLATE_IMAGE_OPTIONAL_HEADER_32B_FIELD(base_of_data); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(image_base); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(section_alignment); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(file_alignment); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_operating_system_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_operating_system_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_image_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_image_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_subsystem_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_subsystem_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(win32_version_value); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_image); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_headers); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(checksum); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(subsystem); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(dll_characteristics); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_stack_reserve); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_stack_commit); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_heap_reserve); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_heap_commit); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(loader_flags); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(number_of_rva_and_sizes); + + ret = PyDict_SetItemString(result, "optional_header", sub); + Py_DECREF(sub); + if (ret != 0) goto failed; + + /* Répertoires de données */ + + if (is_32b) + { + directories = header->optional_header.header_32.data_directory; + number_of_rva_and_sizes = header->optional_header.header_32.number_of_rva_and_sizes; + } + else + { + directories = header->optional_header.header_64.data_directory; + number_of_rva_and_sizes = header->optional_header.header_64.number_of_rva_and_sizes; + } + + dirs = PyTuple_New(number_of_rva_and_sizes); + + for (i = 0; i < number_of_rva_and_sizes; i++) + { + subsub = translate_pe_image_data_directory_to_python(format, directories + i); + if (subsub == NULL) break; + + ret = PyTuple_SetItem(dirs, i, subsub); + if (ret != 0) break; + + } + + if (i < number_of_rva_and_sizes) + goto failed_with_dirs; + + /** + * La fonction PyTuple_SetItem() comporte le prologue suivant : + * + * if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) { + * Py_XDECREF(newitem); + * PyErr_BadInternalCall(); + * return -1; + * } + * + * Comme l'appel à PyDict_SetItemString() incrémente le compte de référence + * de dirs, il convient de le réaliser après la consitution de la liste. + */ + + ret = PyDict_SetItemString(sub, "directories", dirs); + if (ret != 0) goto failed_with_dirs; + + Py_DECREF(dirs); + + return result; + + failed_with_dirs: + + Py_DECREF(dirs); + + failed: + + Py_DECREF(result); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : format = format PE chargé sur lequel s'appuyer. * * dir = répertoire PE à décrire en Python. * * * * Description : Traduit un répertoire PE en Python. * @@ -129,7 +344,7 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h * * ******************************************************************************/ -PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const image_data_directory *dir) +PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const image_data_directory_t *dir) { PyObject *result; /* Construction à retourner */ PyTypeObject *base; /* Modèle d'objet à créer */ @@ -146,6 +361,7 @@ PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const i { \ attrib = PyLong_FromUnsignedLongLong(dir->_f); \ ret = PyDict_SetItemString(result, #_f, attrib); \ + Py_DECREF(attrib); \ if (ret != 0) goto failed; \ } \ while (0); @@ -167,6 +383,73 @@ PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const i /****************************************************************************** * * * Paramètres : format = format PE chargé sur lequel s'appuyer. * +* header = en-tête de section à décrire en Python. * +* * +* Description : Traduit une section PE en Python. * +* * +* Retour : Structure mise en place ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *translate_pe_section_header_to_python(GPeFormat *format, const image_section_header_t *header) +{ + PyObject *result; /* Construction à retourner */ + PyTypeObject *base; /* Modèle d'objet à créer */ + PyObject *attrib; /* Attribut à constituer */ + int ret; /* Bilan d'une mise en place */ + + base = get_python_py_struct_type(); + + result = PyObject_CallFunction((PyObject *)base, NULL); + assert(result != NULL); + + /* Nom de la section */ + + attrib = PyBytes_FromStringAndSize(header->name, IMAGE_SIZEOF_SHORT_NAME); + + ret = PyDict_SetItemString(result, "name", attrib); + Py_DECREF(attrib); + + if (ret != 0) goto failed; + + /* Eléments classiques */ + +#define TRANSLATE_IMAGE_SECTION_HEADER_FIELD(_f) \ + do \ + { \ + attrib = PyLong_FromUnsignedLongLong(header->_f); \ + ret = PyDict_SetItemString(result, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) goto failed; \ + } \ + while (0); + + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(virtual_size); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(virtual_address); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(size_of_raw_data); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_raw_data); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_relocations); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_line_numbers); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(number_of_relocations); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(number_of_line_numbers); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(characteristics); + + return result; + + failed: + + Py_DECREF(result); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : format = format PE chargé sur lequel s'appuyer. * * id = ensemble d'informations à décrire en Python. * * * * Description : Traduit une série d'informations enrichies en Python. * @@ -194,6 +477,7 @@ PyObject *translate_pe_comp_id_to_python(GPeFormat *format, const comp_id_t *id) { \ attrib = PyLong_FromUnsignedLongLong(id->_f); \ ret = PyDict_SetItemString(result, #_f, attrib); \ + Py_DECREF(attrib); \ if (ret != 0) goto failed; \ } \ while (0); diff --git a/plugins/pe/python/translate.h b/plugins/pe/python/translate.h index dbde6c8..e12b4ae 100644 --- a/plugins/pe/python/translate.h +++ b/plugins/pe/python/translate.h @@ -35,10 +35,16 @@ /* Traduit un en-tête MS-DOS en Python. */ -PyObject *translate_pe_dos_header_to_python(GPeFormat *, const image_dos_header *); +PyObject *translate_pe_dos_header_to_python(GPeFormat *, const image_dos_header_t *); + +/* Traduit un en-tête PE en Python. */ +PyObject *translate_pe_nt_headers_to_python(GPeFormat *, const image_nt_headers_t *); /* Traduit un répertoire PE en Python. */ -PyObject *translate_pe_image_data_directory_to_python(GPeFormat *, const image_data_directory *); +PyObject *translate_pe_image_data_directory_to_python(GPeFormat *, const image_data_directory_t *); + +/* Traduit une section PE en Python. */ +PyObject *translate_pe_section_header_to_python(GPeFormat *, const image_section_header_t *); /* Traduit une série d'informations enrichies en Python. */ PyObject *translate_pe_comp_id_to_python(GPeFormat *, const comp_id_t *); diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am index 888de8a..c574727 100644 --- a/plugins/pychrysalide/Makefile.am +++ b/plugins/pychrysalide/Makefile.am @@ -1,7 +1,7 @@ DEFAULT_INCLUDES = -I$(top_builddir) -idirafter. -lib_LTLIBRARIES = pychrysalide.la +lib_LTLIBRARIES = pychrysalide.la pychrysalideui.la libdir = $(pluginslibdir) @@ -15,12 +15,6 @@ endif # if BUILD_GTK_SUPPORT -# GTKEXT_LIBADD = \ -# gtkext/libpychrysagtkext.la - -# GTKEXT_SUBDIR = \ -# gtkext - # GUI_LIBADD = \ # gui/libpychrysagui.la @@ -32,7 +26,10 @@ endif pychrysalide_la_SOURCES = \ access.h access.c \ + bindings.h bindings.c \ constants.h constants.c \ + convert.h convert.c \ + core-int.h \ core.h core.c \ helpers.h helpers.c \ star.h star.c \ @@ -48,9 +45,6 @@ AM_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) $(TOOLKIT_CFL # common/libpychrysacommon.la \ # core/libpychrysacore.la \ # debug/libpychrysadebug.la \ -# format/libpychrysaformat.la \ -# glibext/libpychrysaglibext.la \ -# $(GTKEXT_LIBADD) \ # $(GUI_LIBADD) \ # mangling/libpychrysamangling.la \ # plugins/libpychrysaplugins.la @@ -58,7 +52,10 @@ AM_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) $(TOOLKIT_CFL pychrysalide_la_LIBADD = \ analysis/libpychrysaanalysis4.la \ arch/libpychrysaarch4.la \ + common/libpychrysacommon.la \ core/libpychrysacore.la \ + format/libpychrysaformat.la \ + glibext/libpychrysaglibext.la \ plugins/libpychrysaplugins.la # -ldl: dladdr(), dlerror() @@ -70,10 +67,32 @@ pychrysalide_la_LDFLAGS = \ $(RUN_PATH) +EXTRA_pychrysalideui_la_DEPENDENCIES = pychrysalide.la + +pychrysalideui_la_SOURCES = \ + core-ui-int.h \ + core-ui.h core-ui.c \ + helpers-ui.h helpers-ui.c + +pychrysalideui_la_LIBADD = \ + arch/libpychrysaarchui.la \ + glibext/libpychrysaglibextui.la \ + gtkext/libpychrysagtkext.la + +# -ldl: dladdr(), dlerror() +pychrysalideui_la_LDFLAGS = \ + -module -avoid-version -ldl \ + $(LIBPYTHON_INTERPRETER_LIBS) \ + $(LIBPYGOBJECT_LIBS) \ + -L.libs -l:pychrysalide.so \ + -L$(top_srcdir)/src/.libs -lchrysacoreui\ + $(RUN_PATH) + + devdir = $(includedir)/chrysalide/$(subdir) dev_HEADERS = $(pychrysalide_la_SOURCES:%c=) -#SUBDIRS = analysis arch common core debug format glibext $(GTKEXT_SUBDIR) $(GUI_SUBDIR) mangling plugins -SUBDIRS = analysis arch core plugins +#SUBDIRS = analysis arch common core debug $(GTKEXT_SUBDIR) $(GUI_SUBDIR) mangling plugins +SUBDIRS = analysis arch common core format glibext gtkext plugins diff --git a/plugins/pychrysalide/analysis/content.c b/plugins/pychrysalide/analysis/content.c index dd9c1c1..c271139 100644 --- a/plugins/pychrysalide/analysis/content.c +++ b/plugins/pychrysalide/analysis/content.c @@ -50,9 +50,9 @@ /* Initialise la classe générique des contenus de binaire. */ -static void py_binary_content_init_gclass(GBinContentClass *, gpointer); +static int py_binary_content_init_gclass(GBinContentClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT, py_binary_content_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_binary_content_init(PyObject *, PyObject *, PyObject *); @@ -163,37 +163,39 @@ static PyObject *py_binary_content_get_data(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe générique des contenus de binaire. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_binary_content_init_gclass(GBinContentClass *class, gpointer unused) +static int py_binary_content_init_gclass(GBinContentClass *gclass, PyTypeObject *pyclass) { - class->describe = py_binary_content_describe_wrapper; + PY_CLASS_SET_WRAPPER(gclass->describe, py_binary_content_describe_wrapper); + + PY_CLASS_SET_WRAPPER(gclass->compute_checksum, py_binary_content_compute_checksum_wrapper); - class->compute_checksum = py_binary_content_compute_checksum_wrapper; + PY_CLASS_SET_WRAPPER(gclass->compute_size, py_binary_content_compute_size_wrapper); + PY_CLASS_SET_WRAPPER(gclass->compute_start_pos, py_binary_content_compute_start_pos_wrapper); + PY_CLASS_SET_WRAPPER(gclass->compute_end_pos, py_binary_content_compute_end_pos_wrapper); - class->compute_size = py_binary_content_compute_size_wrapper; - class->compute_start_pos = py_binary_content_compute_start_pos_wrapper; - class->compute_end_pos = py_binary_content_compute_end_pos_wrapper; + PY_CLASS_SET_WRAPPER(gclass->seek, py_binary_content_seek_wrapper); - class->seek = py_binary_content_seek_wrapper; + PY_CLASS_SET_WRAPPER(gclass->read_raw, py_binary_content_read_raw_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u8, py_binary_content_read_u8_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u16, py_binary_content_read_u16_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u32, py_binary_content_read_u32_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u64, py_binary_content_read_u64_wrapper); - class->read_raw = py_binary_content_read_raw_wrapper; - class->read_u8 = py_binary_content_read_u8_wrapper; - class->read_u16 = py_binary_content_read_u16_wrapper; - class->read_u32 = py_binary_content_read_u32_wrapper; - class->read_u64 = py_binary_content_read_u64_wrapper; + PY_CLASS_SET_WRAPPER(gclass->read_uleb128, py_binary_content_read_uleb128_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_leb128, py_binary_content_read_leb128_wrapper); - class->read_uleb128 = py_binary_content_read_uleb128_wrapper; - class->read_leb128 = py_binary_content_read_leb128_wrapper; + return 0; } @@ -2248,6 +2250,8 @@ bool ensure_python_binary_content_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_BIN_CONTENT, (PyGClassInitFunc)py_binary_content_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_BIN_CONTENT, type)) return false; diff --git a/plugins/pychrysalide/analysis/scan/exprs/constants.c b/plugins/pychrysalide/analysis/scan/exprs/constants.c index b11ac4c..adf35bb 100644 --- a/plugins/pychrysalide/analysis/scan/exprs/constants.c +++ b/plugins/pychrysalide/analysis/scan/exprs/constants.c @@ -51,7 +51,7 @@ bool define_literal_expression_value_type_constants(PyTypeObject *type) values = PyDict_New(); - result = add_const_to_group(values, "LVT_BOOLEAN", LVT_BOOLEAN); + result = add_const_to_group(values, "BOOLEAN", LVT_BOOLEAN); if (result) result = add_const_to_group(values, "SIGNED_INTEGER", LVT_SIGNED_INTEGER); if (result) result = add_const_to_group(values, "UNSIGNED_INTEGER", LVT_UNSIGNED_INTEGER); if (result) result = add_const_to_group(values, "STRING", LVT_STRING); @@ -77,7 +77,7 @@ bool define_literal_expression_value_type_constants(PyTypeObject *type) * Paramètres : arg = argument quelconque à tenter de convertir. * * dst = destination des valeurs récupérées en cas de succès. * * * -* Description : Tente de convertir en constante LiteralValueType. * +* Description : Tente de convertir en constante LiteralValueType. * * * * Retour : Bilan de l'opération, voire indications supplémentaires. * * * diff --git a/plugins/pychrysalide/arch/Makefile.am b/plugins/pychrysalide/arch/Makefile.am index d3ee3f0..a0dcfdb 100644 --- a/plugins/pychrysalide/arch/Makefile.am +++ b/plugins/pychrysalide/arch/Makefile.am @@ -1,19 +1,13 @@ -noinst_LTLIBRARIES = libpychrysaarch4.la # libpychrysaarch.la +noinst_LTLIBRARIES = libpychrysaarch4.la libpychrysaarchui.la # libpychrysaarch.la # libpychrysaarch_la_SOURCES = \ -# constants.h constants.c \ # context.h context.c \ # instriter.h instriter.c \ -# instruction.h instruction.c \ -# module.h module.c \ -# operand.h operand.c \ -# processor.h processor.c \ -# register.h register.c \ -# vmpa.h vmpa.c +# processor.h processor.c # libpychrysaarch_la_LIBADD = \ -# instructions/libpychrysaarchinstructions.la \ +# \ # operands/libpychrysaarchoperands.la # libpychrysaarch_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ @@ -22,21 +16,33 @@ noinst_LTLIBRARIES = libpychrysaarch4.la # libpychrysaarch.la libpychrysaarch4_la_SOURCES = \ constants.h constants.c \ + instruction.h instruction.c \ module.h module.c \ + operand.h operand.c \ + register.h register.c \ vmpa.h vmpa.c -# libpychrysaarch4_la_LIBADD = \ -# instructions/libpychrysaarchinstructions.la \ -# operands/libpychrysaarchoperands.la +libpychrysaarch4_la_LIBADD = \ + instructions/libpychrysaarchinstructions.la \ + operands/libpychrysaarchoperands.la libpychrysaarch4_la_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ $(TOOLKIT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT +libpychrysaarchui_la_SOURCES = \ + module-ui.h module-ui.c \ + operand-ui.h operand-ui.c + +libpychrysaarchui_la_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + $(TOOLKIT_CFLAGS) \ + -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT + + devdir = $(includedir)/chrysalide/$(subdir) dev_HEADERS = $(libpychrysaarch_la_SOURCES:%c=) -# SUBDIRS = instructions operands +SUBDIRS = instructions operands diff --git a/plugins/pychrysalide/arch/constants.c b/plugins/pychrysalide/arch/constants.c index 3604795..5db59ff 100644 --- a/plugins/pychrysalide/arch/constants.c +++ b/plugins/pychrysalide/arch/constants.c @@ -25,7 +25,7 @@ #include "constants.h" -//#include <arch/instruction.h> +#include <arch/instruction.h> //#include <arch/processor.h> #include <arch/vmpa.h> @@ -33,7 +33,6 @@ #include "../helpers.h" -#if 0 // FIXME /****************************************************************************** * * @@ -116,6 +115,8 @@ bool define_arch_instruction_constants(PyTypeObject *type) } +#if 0 // FIXME + /****************************************************************************** * * * Paramètres : type = type dont le dictionnaire est à compléter. * diff --git a/plugins/pychrysalide/arch/constants.h b/plugins/pychrysalide/arch/constants.h index b12579e..2f16c4f 100644 --- a/plugins/pychrysalide/arch/constants.h +++ b/plugins/pychrysalide/arch/constants.h @@ -30,10 +30,13 @@ #include <stdbool.h> -#if 0 // FIXME + /* Définit les constantes relatives aux instructions. */ bool define_arch_instruction_constants(PyTypeObject *); + +#if 0 // FIXME + /* Définit les constantes relatives aux processeurs. */ bool define_arch_processor_constants(PyTypeObject *); diff --git a/plugins/pychrysalide/arch/instruction.c b/plugins/pychrysalide/arch/instruction.c index 0a9ba16..49daa9c 100644 --- a/plugins/pychrysalide/arch/instruction.c +++ b/plugins/pychrysalide/arch/instruction.c @@ -27,13 +27,12 @@ #include <assert.h> #include <malloc.h> -#include <string.h> #include <pygobject.h> #include <i18n.h> +#include <plugins/self.h> #include <arch/instruction-int.h> -#include <plugins/dt.h> #include "constants.h" @@ -41,30 +40,38 @@ #include "vmpa.h" #include "../access.h" #include "../helpers.h" -#include "../glibext/linegen.h" - - - -static G_DEFINE_QUARK(cached_keyword, get_cached_keyword); +#include "../glibext/objhole.h" +#include "../glibext/serialize.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_arch_instruction_new(PyTypeObject *, PyObject *, PyObject *); - /* Initialise la classe générique des instructions. */ -static void py_arch_instruction_init_gclass(GArchInstructionClass *, gpointer); +static int py_arch_instruction_init_gclass(GArchInstructionClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(arch_instruction, G_TYPE_ARCH_INSTRUCTION, py_arch_instruction_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(arch_instruction, G_TYPE_ARCH_INSTRUCTION); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_arch_instruction_init(PyObject *, PyObject *, PyObject *); +/* Indique l'encodage d'une instruction de façon détaillée. */ +static char *py_arch_instruction_get_encoding_wrapper(const GArchInstruction *); + /* Fournit le nom humain de l'instruction manipulée. */ -static const char *py_arch_instruction_get_class_keyword(GArchInstruction *); +static char *py_arch_instruction_get_keyword_wrapper(const GArchInstruction *); + + + +/* ------------------- DEFINITION DES LIAISONS ENTRE INSTRUCTIONS ------------------- */ + + +/* Fournit les origines d'une instruction donnée. */ +static PyObject *py_arch_instruction_get_sources(PyObject *, void *); + +/* Fournit les destinations d'une instruction donnée. */ +static PyObject *py_arch_instruction_get_destinations(PyObject *, void *); @@ -72,10 +79,7 @@ static const char *py_arch_instruction_get_class_keyword(GArchInstruction *); /* Attache un opérande supplémentaire à une instruction. */ -static PyObject *py_arch_instruction_attach_extra_operand(PyObject *, PyObject *); - -/* Fournit tous les opérandes d'une instruction. */ -static PyObject *py_arch_instruction_get_operands(PyObject *, void *); +static PyObject *py_arch_instruction_attach_operand(PyObject *, PyObject *); /* Remplace un opérande d'une instruction par un autre. */ static PyObject *py_arch_instruction_replace_operand(PyObject *, PyObject *); @@ -89,34 +93,35 @@ static PyObject *py_arch_instruction_find_operand_path(PyObject *, PyObject *); /* Obtient l'opérande correspondant à un chemin donné. */ static PyObject *py_arch_instruction_get_operand_from_path(PyObject *, PyObject *); +/* Fournit tous les opérandes d'une instruction. */ +static PyObject *py_arch_instruction_get_operands(PyObject *, void *); -/* ------------------- DEFINITION DES LIAISONS ENTRE INSTRUCTIONS ------------------- */ +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ -/* Fournit les origines d'une instruction donnée. */ -static PyObject *py_arch_instruction_get_sources(PyObject *, void *); -/* Fournit les destinations d'une instruction donnée. */ -static PyObject *py_arch_instruction_get_destinations(PyObject *, void *); +/* Ajoute une information complémentaire à une instruction. */ +static PyObject *py_arch_instruction_set_flag(PyObject *, PyObject *); +/* Retire une information complémentaire à une instruction. */ +static PyObject *py_arch_instruction_unset_flag(PyObject *, PyObject *); +/* Détermine si une instruction possède un fanion particulier. */ +static PyObject *py_arch_instruction_has_flag(PyObject *, PyObject *); -/* --------------------- INSTRUCTIONS D'ARCHITECTURES EN PYTHON --------------------- */ +/* Fournit l'identifiant correspondant à un type d'instructions. */ +static PyObject *py_arch_instruction_get_type_id(PyObject *, void *); +/* Indique l'encodage d'une instruction de façon détaillée. */ +static PyObject *py_arch_instruction_get_encoding(PyObject *, void *); -/* Fournit l'identifiant unique pour un ensemble d'instructions. */ -static PyObject *py_arch_instruction_get_unique_id(PyObject *, void *); +/* Indique l'encodage d'une instruction de façon détaillée. */ +static PyObject *py_arch_instruction_get_keyword(PyObject *, void *); /* Fournit la place mémoire d'une instruction. */ static PyObject *py_arch_instruction_get_range(PyObject *, void *); -/* Définit la localisation d'une instruction. */ -static int py_arch_instruction_set_range(PyObject *, PyObject *, void *); - -/* Fournit le nom humain de l'instruction manipulée. */ -static PyObject *py_arch_instruction_get_keyword(PyObject *, void *); - /* ---------------------------------------------------------------------------------- */ @@ -126,24 +131,23 @@ static PyObject *py_arch_instruction_get_keyword(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe générique des instructions. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_arch_instruction_init_gclass(GArchInstructionClass *class, gpointer unused) +static int py_arch_instruction_init_gclass(GArchInstructionClass *gclass, PyTypeObject *pyclass) { - GArchInstructionClass *instr; /* Encore une autre vision... */ - - instr = G_ARCH_INSTRUCTION_CLASS(class); + PY_CLASS_SET_WRAPPER(gclass->get_encoding, py_arch_instruction_get_encoding_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_keyword, py_arch_instruction_get_keyword_wrapper); - instr->get_keyword = (get_instruction_keyword_fc)py_arch_instruction_get_class_keyword; + return 0; } @@ -164,17 +168,25 @@ static void py_arch_instruction_init_gclass(GArchInstructionClass *class, gpoint static int py_arch_instruction_init(PyObject *self, PyObject *args, PyObject *kwds) { - unsigned short int uid; /* Indentifiant unique de type */ - const char *keyword; /* Désignation d'instruction */ + unsigned short int tid; /* Indentifiant unique de type */ int ret; /* Bilan de lecture des args. */ GArchInstruction *instr; /* Instruction à manipuler */ - GQuark cache_key; /* Emplacement local */ - static char *kwlist[] = { "uid", "keyword", NULL }; +#define ARCH_INSTRUCTION_DOC \ + "The ArchInstruction object provides a base class for instructions" \ + " of any architecture.\n" \ + " operands of any kind for new architectures.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect"\ + " one argument: an unique identifier, as an integer value.\n" \ + "\n" \ + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.arch.ArchRegister._get_encoding();\n" \ + "* pychrysalide.arch.ArchRegister._get_keyword().\n" /* Récupération des paramètres */ - ret = PyArg_ParseTupleAndKeywords(args, kwds, "Hs", kwlist, &uid, &keyword); + ret = PyArg_ParseTuple(args, "H", &tid); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -186,13 +198,72 @@ static int py_arch_instruction_init(PyObject *self, PyObject *args, PyObject *kw instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - cache_key = get_cached_keyword_quark(); + if (!g_arch_instruction_create(instr, tid)) + return -1; + + return 0; - g_object_set_qdata_full(G_OBJECT(instr), cache_key, strdup(keyword), g_free); +} - g_arch_instruction_set_unique_id(G_ARCH_INSTRUCTION(instr), uid); - return 0; +/****************************************************************************** +* * +* Paramètres : instr = instruction quelconque à consulter. * +* * +* Description : Indique l'encodage d'une instruction de façon détaillée. * +* * +* Retour : Description humaine de l'encodage utilisé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *py_arch_instruction_get_encoding_wrapper(const GArchInstruction *instr) +{ + char *result; /* Encodage à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_GET_ENCODING_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_encoding, "$self, /", \ + METH_NOARGS, \ + "Abstract method describing the encoding related to an" \ + " instruction.\n" \ + "\n" \ + "The result should be the string value.\n" \ +) + + result = NULL; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(instr)); + + if (has_python_method(pyobj, "_get_encoding")) + { + pyret = run_python_method(pyobj, "_get_encoding", NULL); + + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + log_variadic_message(LMT_ERROR, _("The returned raw name must be a string")); + + else + result = strdup(PyUnicode_DATA(pyret)); + + } + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; } @@ -209,15 +280,50 @@ static int py_arch_instruction_init(PyObject *self, PyObject *args, PyObject *kw * * ******************************************************************************/ -static const char *py_arch_instruction_get_class_keyword(GArchInstruction *instr) +static char *py_arch_instruction_get_keyword_wrapper(const GArchInstruction *instr) { - const char *result; /* Désignation à retourner */ - GQuark cache_key; /* Emplacement local */ + char *result; /* Etiquette à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_GET_KEYWORD_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_keyword, "$self, /", \ + METH_NOARGS, \ + "Abstract method giving the official name of the assembly" \ + " instruction.\n" \ + "\n" \ + "The result should be the string value.\n" \ +) + + result = NULL; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(instr)); + + if (has_python_method(pyobj, "_get_keyword")) + { + pyret = run_python_method(pyobj, "_get_keyword", NULL); + + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + log_variadic_message(LMT_ERROR, _("The returned raw name must be a string")); - cache_key = get_cached_keyword_quark(); + else + result = strdup(PyUnicode_DATA(pyret)); - result = g_object_get_qdata(G_OBJECT(instr), cache_key); - assert(result != NULL); + } + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); return result; @@ -226,94 +332,201 @@ static const char *py_arch_instruction_get_class_keyword(GArchInstruction *instr /* ---------------------------------------------------------------------------------- */ -/* MANIPULATION DES OPERANDES */ +/* DEFINITION DES LIAISONS ENTRE INSTRUCTIONS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : self = architecture concernée par la procédure. * -* args = instruction représentant le point de départ. * +* Paramètres : self = instruction d'architecture à manipuler. * +* unused = adresse non utilisée ici. * * * -* Description : Attache un opérande supplémentaire à une instruction. * +* Description : Fournit les origines d'une instruction donnée. * * * -* Retour : None. * +* Retour : Nombre de ces origines. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_attach_extra_operand(PyObject *self, PyObject *args) +static PyObject *py_arch_instruction_get_sources(PyObject *self, void *unused) { - GArchOperand *op; /* Opérande concerné à ajouter */ - int ret; /* Bilan de lecture des args. */ - GArchInstruction *instr; /* Instruction manipulée */ + PyObject *result; /* Instance à retourner */ + GArchInstruction *instr; /* Version native */ + size_t count; /* Nombre de liens présents */ + size_t i; /* Boucle de parcours */ - ret = PyArg_ParseTuple(args, "O&", convert_to_arch_operand, &op); - if (!ret) return NULL; + + GArchInstruction *src; /* Instruction en source */ + InstructionLinkType src_type; /* Type de lien */ + PyObject *linked; /* Source de lien Python */ + PyObject *lnk_type; /* Nature du lien en Python */ +#ifndef NDEBUG + int ret; /* Bilan d'une écriture d'arg. */ +#endif + +#define ARCH_INSTRUCTION_SOURCES_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + sources, py_arch_instruction, \ + "Provide the instructions list driving to the current instruction.\n" \ + "\n" \ + "Each item of the resulting tuple is a pair of" \ + " pychrysalide.arch.ArchInstruction instance and" \ + " pychrysalide.arch.ArchInstruction.InstructionLinkType value." \ +) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_object_ref(G_OBJECT(op)); + g_thick_object_lock(G_THICK_OBJECT(instr)); + + count = g_arch_instruction_count_src_links(instr); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + { + src = g_arch_instruction_get_linked_source(instr, i, &src_type); + + linked = pygobject_new(G_OBJECT(src)); + lnk_type = cast_with_constants_group_from_type(get_python_arch_instruction_type(), + "InstructionLinkType", src_type); + +#ifndef NDEBUG + ret = PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, lnk_type)); + assert(ret == 0); +#else + PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, lnk_type)); +#endif + + unref_object(src); + + } - g_arch_instruction_attach_extra_operand(instr, op); + g_thick_object_unlock(G_THICK_OBJECT(instr)); - Py_RETURN_NONE; + return result; } /****************************************************************************** * * -* Paramètres : self = objet représentant une instruction. * +* Paramètres : self = instruction d'architecture à manipuler. * * unused = adresse non utilisée ici. * * * -* Description : Fournit tous les opérandes d'une instruction. * +* Description : Fournit les destinations d'une instruction donnée. * * * -* Retour : Valeur associée à la propriété consultée. * +* Retour : Nombre de ces destinations. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_operands(PyObject *self, void *unused) +static PyObject *py_arch_instruction_get_destinations(PyObject *self, void *unused) { PyObject *result; /* Instance à retourner */ GArchInstruction *instr; /* Version native */ - size_t count; /* Nombre d'opérandes présents */ + size_t count; /* Nombre de liens présents */ size_t i; /* Boucle de parcours */ - GArchOperand *operand; /* Opérande à manipuler */ - PyObject *opobj; /* Version Python */ + GArchInstruction *dest; /* Instruction en source */ + InstructionLinkType dest_type; /* Type de lien */ + PyObject *linked; /* Destination de lien Python */ + PyObject *lnk_type; /* Nature du lien en Python */ #ifndef NDEBUG int ret; /* Bilan d'une écriture d'arg. */ #endif +#define ARCH_INSTRUCTION_DESTINATIONS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + destinations, py_arch_instruction, \ + "Provide the instructions list following the current instruction.\n" \ + "\n" \ + "Each item of the resulting tuple is a pair of" \ + " pychrysalide.arch.ArchInstruction instance and" \ + " pychrysalide.arch.ArchInstruction.InstructionLinkType value." \ +) + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_lock_operands(instr); + g_thick_object_lock(G_THICK_OBJECT(instr)); - count = _g_arch_instruction_count_operands(instr); + count = g_arch_instruction_count_dest_links(instr); result = PyTuple_New(count); for (i = 0; i < count; i++) { - operand = _g_arch_instruction_get_operand(instr, i); + dest = g_arch_instruction_get_linked_destination(instr, i, &dest_type); - opobj = pygobject_new(G_OBJECT(operand)); + linked = pygobject_new(G_OBJECT(dest)); + lnk_type = cast_with_constants_group_from_type(get_python_arch_instruction_type(), + "InstructionLinkType", dest_type); #ifndef NDEBUG - ret = PyTuple_SetItem(result, i, Py_BuildValue("O", opobj)); + ret = PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, lnk_type)); assert(ret == 0); #else - PyTuple_SetItem(result, i, Py_BuildValue("O", opobj)); + PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, lnk_type)); #endif - g_object_unref(G_OBJECT(operand)); + unref_object(dest); } - g_arch_instruction_unlock_operands(instr); + g_thick_object_unlock(G_THICK_OBJECT(instr)); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DES OPERANDES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Attache un opérande supplémentaire à une instruction. * +* * +* Retour : None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_instruction_attach_operand(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GArchOperand *op; /* Opérande concerné à ajouter */ + int ret; /* Bilan de lecture des args. */ + GArchInstruction *instr; /* Instruction manipulée */ + +#define ARCH_INSTRUCTION_ATTACH_OPERAND_METHOD PYTHON_METHOD_DEF \ +( \ + attach_operand, "$self, operand, /", \ + METH_VARARGS, py_arch_instruction, \ + "Add an extra operand to an instruction.\n" \ + "\n" \ + "The instruction has to be locked during the instruction.\n" \ + "\n" \ + "The *operand* argument has to be a pychrysalide.arch.ArchOperand" \ + " instance." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_arch_operand, &op); + if (!ret) return NULL; + + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); + + g_arch_instruction_attach_operand(instr, op); + + result = Py_None; + Py_INCREF(result); return result; @@ -342,6 +555,21 @@ static PyObject *py_arch_instruction_replace_operand(PyObject *self, PyObject *a GArchInstruction *instr; /* Instruction manipulée */ bool status; /* Bilan de l'opération */ +#define ARCH_INSTRUCTION_REPLACE_OPERAND_METHOD PYTHON_METHOD_DEF \ +( \ + replace_operand, "$self, old, new, /", \ + METH_VARARGS, py_arch_instruction, \ + "Replace an old instruction operand by a another one.\n" \ + "\n" \ + "The instruction has to be locked during the instruction.\n" \ + "\n" \ + "Both the *old* and *new* arguments have to be a" \ + " pychrysalide.arch.ArchOperand instance.\n" \ + "\n" \ + "The status of the operation is returned as a boolean value: *True*"\ + " if the operand has been replaced, *False* in case of failure." \ +) + ret = PyArg_ParseTuple(args, "O&O&", convert_to_arch_operand, &old, convert_to_arch_operand, &new); if (!ret) return NULL; @@ -349,9 +577,6 @@ static PyObject *py_arch_instruction_replace_operand(PyObject *self, PyObject *a status = g_arch_instruction_replace_operand(instr, old, new); - if (status) - g_object_ref(G_OBJECT(new)); - result = status ? Py_True : Py_False; Py_INCREF(result); @@ -381,6 +606,21 @@ static PyObject *py_arch_instruction_detach_operand(PyObject *self, PyObject *ar GArchInstruction *instr; /* Instruction manipulée */ bool status; /* Bilan de l'opération */ +#define ARCH_INSTRUCTION_DETACH_OPERAND_METHOD PYTHON_METHOD_DEF \ +( \ + detach_operand, "$self, operand, /", \ + METH_VARARGS, py_arch_instruction, \ + "Remove an operand from the instruction.\n" \ + "\n" \ + "The instruction has to be locked during the instruction.\n" \ + "\n" \ + "The *operand* argument has to be a pychrysalide.arch.ArchOperand" \ + " instance.\n" \ + "\n" \ + "The status of the operation is returned as a boolean value: *True*"\ + " if the operand has been removed, *False* in case of failure." \ +) + ret = PyArg_ParseTuple(args, "O&", convert_to_arch_operand, &target); if (!ret) return NULL; @@ -500,7 +740,7 @@ static PyObject *py_arch_instruction_get_operand_from_path(PyObject *self, PyObj if (op != NULL) { result = pygobject_new(G_OBJECT(op)); - g_object_unref(G_OBJECT(op)); + unref_object(op); } else { @@ -513,182 +753,258 @@ static PyObject *py_arch_instruction_get_operand_from_path(PyObject *self, PyObj } - -/* ---------------------------------------------------------------------------------- */ -/* DEFINITION DES LIAISONS ENTRE INSTRUCTIONS */ -/* ---------------------------------------------------------------------------------- */ - - /****************************************************************************** * * -* Paramètres : self = instruction d'architecture à manipuler. * +* Paramètres : self = objet représentant une instruction. * * unused = adresse non utilisée ici. * * * -* Description : Fournit les origines d'une instruction donnée. * +* Description : Fournit tous les opérandes d'une instruction. * * * -* Retour : Nombre de ces origines. * +* Retour : Valeur associée à la propriété consultée. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_sources(PyObject *self, void *unused) +static PyObject *py_arch_instruction_get_operands(PyObject *self, void *unused) { PyObject *result; /* Instance à retourner */ GArchInstruction *instr; /* Version native */ - size_t count; /* Nombre de liens présents */ + size_t count; /* Nombre d'opérandes présents */ size_t i; /* Boucle de parcours */ - const instr_link_t *source; /* Origine des liens */ - PyObject *linked; /* Source de lien Python */ - PyObject *type; /* Nature du lien en Python */ + GArchOperand *operand; /* Opérande à manipuler */ + PyObject *opobj; /* Version Python */ #ifndef NDEBUG int ret; /* Bilan d'une écriture d'arg. */ #endif -#define ARCH_INSTRUCTION_SOURCES_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - sources, py_arch_instruction, \ - "Provide the instructions list driving to the current instruction.\n" \ - "\n" \ - "Each item of the resulting tuple is a pair of" \ - " pychrysalide.arch.ArchInstruction instance and" \ - " pychrysalide.arch.ArchInstruction.InstructionLinkType value." \ +#define ARCH_INSTRUCTION_OPERANDS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + operands, py_arch_instruction, \ + "List of instruction attached operands.\n" \ + "\n" \ + "The result is a list of pychrysalide.arch.ArchOperand" \ + " instances, which can be empty." \ ) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_lock_src(instr); + g_thick_object_lock(G_THICK_OBJECT(instr)); - count = g_arch_instruction_count_sources(instr); + count = g_arch_instruction_count_operands(instr); result = PyTuple_New(count); for (i = 0; i < count; i++) { - source = g_arch_instruction_get_source(instr, i); + operand = g_arch_instruction_get_operand(instr, i); - linked = pygobject_new(G_OBJECT(source->linked)); - type = cast_with_constants_group_from_type(get_python_arch_instruction_type(), - "InstructionLinkType", source->type); + opobj = pygobject_new(G_OBJECT(operand)); #ifndef NDEBUG - ret = PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, type)); + ret = PyTuple_SetItem(result, i, Py_BuildValue("O", opobj)); assert(ret == 0); #else - PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, type)); + PyTuple_SetItem(result, i, Py_BuildValue("O", opobj)); #endif - unref_instr_link(source); + unref_object(operand); } - g_arch_instruction_unlock_src(instr); + g_thick_object_unlock(G_THICK_OBJECT(instr)); return result; } + +/* ---------------------------------------------------------------------------------- */ +/* LIAISON DE FONCTIONNALITES AVEC L'API PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : self = instruction d'architecture à manipuler. * -* unused = adresse non utilisée ici. * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * * * -* Description : Fournit les destinations d'une instruction donnée. * +* Description : Ajoute une information complémentaire à une instruction. * * * -* Retour : Nombre de ces destinations. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_destinations(PyObject *self, void *unused) +static PyObject *py_arch_instruction_set_flag(PyObject *self, PyObject *args) { - PyObject *result; /* Instance à retourner */ - GArchInstruction *instr; /* Version native */ - size_t count; /* Nombre de liens présents */ - size_t i; /* Boucle de parcours */ - const instr_link_t *dest; /* Destination des liens */ - PyObject *linked; /* Destination de lien Python */ - PyObject *type; /* Nature du lien en Python */ -#ifndef NDEBUG - int ret; /* Bilan d'une écriture d'arg. */ -#endif - -#define ARCH_INSTRUCTION_DESTINATIONS_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - destinations, py_arch_instruction, \ - "Provide the instructions list following the current instruction.\n" \ - "\n" \ - "Each item of the resulting tuple is a pair of" \ - " pychrysalide.arch.ArchInstruction instance and" \ - " pychrysalide.arch.ArchInstruction.InstructionLinkType value." \ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchInstruction *instr; /* Instruction manipulée */ + bool status; /* Bilan à transmettre */ + +#define ARCH_INSTRUCTION_SET_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + set_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_instruction, \ + "Add some flags to the instruction.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to apply to the instruction state.\n" \ + "\n" \ + "The result is an boolean status: *True* for operation" \ + " success, *False* otherwise." \ ) + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_lock_dest(instr); + status = g_arch_instruction_set_flag(instr, flag); - count = g_arch_instruction_count_destinations(instr); + result = status ? Py_True : Py_False; + Py_INCREF(result); - result = PyTuple_New(count); + return result; - for (i = 0; i < count; i++) - { - dest = g_arch_instruction_get_destination(instr, i); +} - linked = pygobject_new(G_OBJECT(dest->linked)); - type = cast_with_constants_group_from_type(get_python_arch_instruction_type(), - "InstructionLinkType", dest->type); -#ifndef NDEBUG - ret = PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, type)); - assert(ret == 0); -#else - PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, type)); -#endif +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Retire une information complémentaire à une instruction. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_instruction_unset_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchInstruction *instr; /* Instruction manipulée */ + bool status; /* Bilan à transmettre */ + +#define ARCH_INSTRUCTION_UNSET_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + unset_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_instruction, \ + "Remove some flags from the instruction.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to delete from the instruction state.\n" \ + "\n" \ + "The result is an boolean status: *True* for operation" \ + " success, *False* otherwise." \ +) - unref_instr_link(dest); + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; - } + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_unlock_dest(instr); + status = g_arch_instruction_unset_flag(instr, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); return result; } +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Détermine si une instruction possède un fanion particulier. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ -/* ---------------------------------------------------------------------------------- */ -/* INSTRUCTIONS D'ARCHITECTURES EN PYTHON */ -/* ---------------------------------------------------------------------------------- */ +static PyObject *py_arch_instruction_has_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchInstruction *instr; /* Instruction manipulée */ + bool status; /* Bilan à transmettre */ + +#define ARCH_INSTRUCTION_HAS_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + has_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_instruction, \ + "Tell if some flags are set for the instruction.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to test for the instruction state.\n" \ + "\n" \ + "The result is an boolean status: *True* if the bits" \ + " are active, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); + + status = g_arch_instruction_has_flag(instr, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} /****************************************************************************** * * -* Paramètres : self = classe représentant une instruction. * -* closure = adresse non utilisée ici. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Fournit l'identifiant unique pour un ensemble d'instructions.* +* Description : Fournit l'identifiant correspondant à un type d'instructions.* * * -* Retour : Identifiant unique par type d'instruction. * +* Retour : Identifiant unique par type d'instruction et architecture. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_unique_id(PyObject *self, void *closure) +static PyObject *py_arch_instruction_get_type_id(PyObject *self, void *closure) { - PyObject *result; /* Conversion à retourner */ - GArchInstruction *instr; /* Version native */ - itid_t uid; /* Identifiant unique associé */ + PyObject *result; /* Valeur à retourner */ + GArchInstruction *instr; /* Instruction manipulée */ + itid_t tid; /* Identifiant à transmettre */ + +#define ARCH_INSTRUCTION_TYPE_ID_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + type_id, py_arch_instruction, \ + "Provide the unique identifier given to this kind of" \ + " instruction.\n" \ + "\n" \ + "The returned value is an integer." \ +) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - uid = g_arch_instruction_get_unique_id(instr); + tid = g_arch_instruction_get_type_id(instr); - result = PyLong_FromUnsignedLong(uid); + result = PyLong_FromUnsignedLong(tid); return result; @@ -697,27 +1013,61 @@ static PyObject *py_arch_instruction_get_unique_id(PyObject *self, void *closure /****************************************************************************** * * -* Paramètres : self = classe représentant une instruction. * -* closure = adresse non utilisée ici. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Fournit la place mémoire d'une instruction. * +* Description : Indique l'encodage d'une instruction de façon détaillée. * * * -* Retour : Valeur associée à la propriété consultée. * +* Retour : Description humaine de l'encodage utilisé. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_range(PyObject *self, void *closure) +static PyObject *py_arch_instruction_get_encoding(PyObject *self, void *closure) { - PyObject *result; /* Conversion à retourner */ - GArchInstruction *instr; /* Version native */ - const mrange_t *range; /* Espace mémoire à exporter */ + PyObject *result; /* Valeur à retourner */ + GArchInstruction *instr; /* Instruction manipulée */ + char *encoding; /* Encodage d'une instruction */ + +#define ARCH_INSTRUCTION_ENCODING_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + encoding, py_arch_instruction, \ + "Describe the encoding related to an instruction.\n" \ + "\n" \ + "The returned value is an arbitrary string value." \ +) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - range = g_arch_instruction_get_range(instr); - result = build_from_internal_mrange(range); + encoding = g_arch_instruction_get_encoding(instr); + + if (encoding != NULL) + { + result = PyUnicode_FromString(encoding); + + free(encoding); + + } + + else + { + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as encoding")); + + result = NULL; + + } return result; @@ -727,59 +1077,108 @@ static PyObject *py_arch_instruction_get_range(PyObject *self, void *closure) /****************************************************************************** * * * Paramètres : self = objet Python concerné par l'appel. * -* value = valeur fournie à intégrer ou prendre en compte. * -* closure = adresse non utilisée ici. * +* closure = non utilisé ici. * * * -* Description : Définit la localisation d'une instruction. * +* Description : Indique l'encodage d'une instruction de façon détaillée. * * * -* Retour : Bilan de l'opération pour Python. * +* Retour : Description humaine de l'encodage utilisé. * * * * Remarques : - * * * ******************************************************************************/ -static int py_arch_instruction_set_range(PyObject *self, PyObject *value, void *closure) +static PyObject *py_arch_instruction_get_keyword(PyObject *self, void *closure) { - int ret; /* Bilan d'analyse */ - mrange_t *range; /* Espace mémoire à manipuler */ - GArchInstruction *instr; /* Version native */ + PyObject *result; /* Valeur à retourner */ + GArchInstruction *instr; /* Instruction manipulée */ + char *keyword; /* Désignation d'une instruct° */ + +#define ARCH_INSTRUCTION_KEYWORD_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + keyword, py_arch_instruction, \ + "Give the official name of the assembly instruction.\n" \ + "\n" \ + "The returned value is a string value." \ +) - ret = PyObject_IsInstance(value, (PyObject *)get_python_mrange_type()); - if (!ret) return -1; + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - range = get_internal_mrange(value); + keyword = g_arch_instruction_get_keyword(instr); - instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_set_range(instr, range); + if (keyword != NULL) + { + result = PyUnicode_FromString(keyword); - return 0; + free(keyword); + + } + + else + { + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as keyword")); + + result = NULL; + + } + + return result; } /****************************************************************************** * * -* Paramètres : self = classe représentant une instruction. * -* unused = adresse non utilisée ici. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Fournit le nom humain de l'instruction manipulée. * +* Description : Fournit la place mémoire d'une instruction. * * * -* Retour : Valeur associée à la propriété consultée. * +* Retour : Définition de localisation ou *None*. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_keyword(PyObject *self, void *unused) +static PyObject *py_arch_instruction_get_range(PyObject *self, void *closure) { - PyObject *result; /* Trouvailles à retourner */ - GArchInstruction *instr; /* Version native */ - const char *kw; /* Valeur récupérée */ + PyObject *result; /* Valeur à retourner */ + GArchInstruction *instr; /* Instruction manipulée */ + mrange_t range; /* Localisation d'instruction */ + bool valid; /* Validité de la localisation */ + +#define ARCH_INSTRUCTION_RANGE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + range, py_arch_instruction, \ + "Give access to the memory range covered by the" \ + " current instruction.\n" \ + "\n" \ + "The returned value is a pychrysalide.arch.mrange" \ + " instance or *None* if no location is currently" \ + " defined." \ +) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - kw = g_arch_instruction_get_keyword(instr); - result = PyUnicode_FromString(kw); + valid = g_arch_instruction_get_range(instr, &range); + + if (valid) + result = build_from_internal_mrange(&range); + else + { + result = Py_None; + Py_INCREF(result); + } return result; @@ -801,45 +1200,25 @@ static PyObject *py_arch_instruction_get_keyword(PyObject *self, void *unused) PyTypeObject *get_python_arch_instruction_type(void) { static PyMethodDef py_arch_instruction_methods[] = { - { - "attach_operand", py_arch_instruction_attach_extra_operand, - METH_VARARGS, - "attach_operand($self, op, /)\n--\n\nAdd a new operand to the instruction." - }, - { - "replace_operand", py_arch_instruction_replace_operand, - METH_VARARGS, - "replace_operand($self, old, new, /)\n--\n\nReplace an old instruction operand by a another one." - }, - { - "detach_operand", py_arch_instruction_detach_operand, - METH_VARARGS, - "detach_operand($self, target, /)\n--\n\nRemove an operand from the instruction." - }, + ARCH_INSTRUCTION_ATTACH_OPERAND_METHOD, + ARCH_INSTRUCTION_REPLACE_OPERAND_METHOD, + ARCH_INSTRUCTION_DETACH_OPERAND_METHOD, ARCH_INSTRUCTION_FIND_OPERAND_PATH_METHOD, ARCH_INSTRUCTION_GET_OPERAND_FROM_PATH_METHOD, + ARCH_INSTRUCTION_SET_FLAG_METHOD, + ARCH_INSTRUCTION_UNSET_FLAG_METHOD, + ARCH_INSTRUCTION_HAS_FLAG_METHOD, { NULL } }; static PyGetSetDef py_arch_instruction_getseters[] = { - { - "uid", py_arch_instruction_get_unique_id, NULL, - "Provide the unique identification number given to this kind of instruction.", NULL - }, - { - "range", py_arch_instruction_get_range, py_arch_instruction_set_range, - "Give access to the memory range covered by the current instruction.", NULL - }, - { - "keyword", (getter)py_arch_instruction_get_keyword, (setter)NULL, - "Give le name of the assembly instruction.", NULL - }, - { - "operands", (getter)py_arch_instruction_get_operands, (setter)NULL, - "Provide the list of instruction attached operands.", NULL - }, ARCH_INSTRUCTION_SOURCES_ATTRIB, ARCH_INSTRUCTION_DESTINATIONS_ATTRIB, + ARCH_INSTRUCTION_OPERANDS_ATTRIB, + ARCH_INSTRUCTION_TYPE_ID_ATTRIB, + ARCH_INSTRUCTION_ENCODING_ATTRIB, + ARCH_INSTRUCTION_KEYWORD_ATTRIB, + ARCH_INSTRUCTION_RANGE_ATTRIB, { NULL } }; @@ -852,7 +1231,7 @@ PyTypeObject *get_python_arch_instruction_type(void) .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_BASETYPE, - .tp_doc = "PyChrysalide instruction for a given architecture.", + .tp_doc = ARCH_INSTRUCTION_DOC, .tp_methods = py_arch_instruction_methods, .tp_getset = py_arch_instruction_getseters, @@ -893,9 +1272,14 @@ bool ensure_python_arch_instruction_is_registered(void) dict = PyModule_GetDict(module); - if (!ensure_python_line_generator_is_registered()) + if (!ensure_python_thick_object_is_registered()) return false; + if (!ensure_python_serializable_object_is_registered()) + return false; + + pyg_register_class_init(G_TYPE_ARCH_INSTRUCTION, (PyGClassInitFunc)py_arch_instruction_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_ARCH_INSTRUCTION, type)) return false; diff --git a/plugins/pychrysalide/arch/instructions/Makefile.am b/plugins/pychrysalide/arch/instructions/Makefile.am index 65efe42..29c2a45 100644 --- a/plugins/pychrysalide/arch/instructions/Makefile.am +++ b/plugins/pychrysalide/arch/instructions/Makefile.am @@ -7,7 +7,8 @@ libpychrysaarchinstructions_la_SOURCES = \ raw.h raw.c \ undefined.h undefined.c -libpychrysaarchinstructions_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpychrysaarchinstructions_la_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + $(TOOLKIT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/arch/instructions/constants.c b/plugins/pychrysalide/arch/instructions/constants.c index af7baa9..257c501 100644 --- a/plugins/pychrysalide/arch/instructions/constants.c +++ b/plugins/pychrysalide/arch/instructions/constants.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * constants.c - ajout des constantes de base pour les instructions * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -112,3 +112,59 @@ bool define_undefined_instruction_constants(PyTypeObject *type) return result; } + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en constante ExpectedBehavior. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_undefined_expected_behavior(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + unsigned long value; /* Valeur récupérée */ + + result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to ExpectedBehavior"); + break; + + case 1: + value = PyLong_AsUnsignedLong(arg); + + if (value > IEB_RESERVED) + { + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to ExpectedBehavior"); + result = 0; + } + + else + *((InstrExpectedBehavior *)dst) = value; + + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/arch/instructions/constants.h b/plugins/pychrysalide/arch/instructions/constants.h index 2f0c587..b6ef9a4 100644 --- a/plugins/pychrysalide/arch/instructions/constants.h +++ b/plugins/pychrysalide/arch/instructions/constants.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * constants.h - prototypes pour l'ajout des constantes de base pour les instructions * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -37,6 +37,9 @@ bool define_raw_instruction_constants(PyTypeObject *); /* Définit les constantes liées aux comportements erratiques. */ bool define_undefined_instruction_constants(PyTypeObject *); +/* Tente de convertir en constante ExpectedBehavior. */ +int convert_to_undefined_expected_behavior(PyObject *, void *); + #endif /* _PLUGINS_PYCHRYSALIDE_ARCH_INSTRUCTIONS_CONSTANTS_H */ diff --git a/plugins/pychrysalide/arch/instructions/raw.c b/plugins/pychrysalide/arch/instructions/raw.c index 7e58b96..ae730e8 100644 --- a/plugins/pychrysalide/arch/instructions/raw.c +++ b/plugins/pychrysalide/arch/instructions/raw.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * raw.c - équivalent Python du fichier "arch/instructions/raw.h" * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,26 +28,33 @@ #include <pygobject.h> -#include <i18n.h> -#include <arch/instructions/raw.h> -#include <plugins/dt.h> +#include <arch/instructions/raw-int.h> #include "constants.h" #include "../instruction.h" #include "../vmpa.h" #include "../../access.h" +#include "../../constants.h" #include "../../helpers.h" #include "../../analysis/content.h" +#include "../../glibext/portion.h" -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_raw_instruction_new(PyTypeObject *, PyObject *, PyObject *); +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(raw_instruction, G_TYPE_RAW_INSTRUCTION); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_raw_instruction_init(PyObject *, PyObject *, PyObject *); + + +/* ------------------------ FONCTIONNALITES DE L'INSTRUCTION ------------------------ */ + + /* Indique si le contenu de l'instruction est du bourrage. */ static PyObject *py_raw_instruction_get_padding(PyObject *, void *); @@ -62,64 +69,9 @@ static int py_raw_instruction_set_string(PyObject *, PyObject *, void *); -/****************************************************************************** -* * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_raw_instruction_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_raw_instruction_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_RAW_INSTRUCTION, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** @@ -139,20 +91,17 @@ static PyObject *py_raw_instruction_new(PyTypeObject *type, PyObject *args, PyOb static int py_raw_instruction_init(PyObject *self, PyObject *args, PyObject *kwds) { int result; /* Bilan à retourner */ + GBinaryPortion *area; /* Zone de contenance */ vmpa2t *addr; /* Texte de lecture */ - unsigned long mem_size; /* Taille de portion brute */ + MemoryDataSize size; /* Taille de portion brute */ unsigned long long value; /* Valeur brute à considérer */ GBinContent *content; /* Contenu à lire au besoin */ unsigned long count; /* Nombre d'éléments à lister */ - unsigned int endian; /* Type de boutisme impliqué */ + SourceEndian endian; /* Type de boutisme impliqué */ int ret; /* Bilan de lecture des args. */ - GArchInstruction *fake; /* Instruction à copier */ - GArchInstruction *instr; /* Instruction à manipuler */ - size_t op_count; /* Nombre d'opérande à copier */ - size_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à transférer */ + GRawInstruction *instr; /* Instruction à manipuler */ - static char *kwlist[] = { "addr", "mem_size", "value", "content", "count", "endian", NULL }; + static char *kwlist[] = { "area", "addr", "size", "value", "content", "count", "endian", NULL }; #define RAW_INSTRUCTION_DOC \ "The RawInstruction object handles data which is not (yet?) disassembled" \ @@ -187,9 +136,14 @@ static int py_raw_instruction_init(PyObject *self, PyObject *args, PyObject *kwd count = 0; endian = 0; - ret = PyArg_ParseTupleAndKeywords(args, kwds, "O&k|KO&kI", kwlist, - convert_any_to_vmpa, &addr, &mem_size, - &value, convert_to_binary_content, &content, &count, &endian); + ret = PyArg_ParseTupleAndKeywords(args, kwds, "O&O&O&|KO&kO&", kwlist, + convert_to_binary_portion, &area, + convert_any_to_vmpa, &addr, + convert_to_memory_data_size, &size, + &value, + convert_to_binary_content, &content, + &count, + convert_to_source_endian, &endian); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -199,35 +153,19 @@ static int py_raw_instruction_init(PyObject *self, PyObject *args, PyObject *kwd /* Eléments de base */ - if (content != NULL) - fake = g_raw_instruction_new_array(content, mem_size, count, addr, endian); - else - fake = g_raw_instruction_new_from_value(addr, mem_size, value); + instr = G_RAW_INSTRUCTION(pygobject_get(self)); - if (fake == NULL) + if (content != NULL) { - PyErr_SetString(PyExc_ValueError, _("Unable to build the object with the given parameters.")); - goto clean_exit; + if (!g_raw_instruction_create_array(instr, area, addr, size, content, count, endian)) + goto clean_exit; } - - instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - - g_arch_instruction_lock_operands(fake); - - op_count = _g_arch_instruction_count_operands(fake); - - for (i = 0; i < op_count; i++) + else { - op = _g_arch_instruction_get_operand(fake, i); - g_arch_instruction_attach_extra_operand(instr, op); + if (!g_raw_instruction_create_value(instr, area, addr, size, value)) + goto clean_exit; } - g_arch_instruction_unlock_operands(fake); - - g_arch_instruction_set_range(instr, g_arch_instruction_get_range(fake)); - - g_object_unref(G_OBJECT(fake)); - result = 0; clean_exit: @@ -239,6 +177,12 @@ static int py_raw_instruction_init(PyObject *self, PyObject *args, PyObject *kwd } + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES DE L'INSTRUCTION */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : self = classe représentant une instruction. * @@ -271,7 +215,6 @@ static PyObject *py_raw_instruction_get_padding(PyObject *self, void *closure) result = state ? Py_True : Py_False; Py_INCREF(result); - return result; } @@ -342,7 +285,6 @@ static PyObject *py_raw_instruction_get_string(PyObject *self, void *closure) result = state ? Py_True : Py_False; Py_INCREF(result); - return result; } diff --git a/plugins/pychrysalide/arch/instructions/undefined.c b/plugins/pychrysalide/arch/instructions/undefined.c index 1246daa..1c2bccc 100644 --- a/plugins/pychrysalide/arch/instructions/undefined.c +++ b/plugins/pychrysalide/arch/instructions/undefined.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * undefined.c - équivalent Python du fichier "arch/instructions/undefined.h" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,9 +28,7 @@ #include <pygobject.h> -#include <i18n.h> #include <arch/instructions/undefined-int.h> -#include <plugins/dt.h> #include "constants.h" @@ -40,75 +38,27 @@ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_undef_instruction_new(PyTypeObject *, PyObject *, PyObject *); +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Initialise une instance sur la base du dérivé de GObject. */ -static int py_undef_instruction_init(PyObject *, PyObject *, PyObject *); - -/* Indique le type de conséquences réél de l'instruction. */ -static PyObject *py_undef_instruction_get_behavior(PyObject *, void *); - - - -/****************************************************************************** -* * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_undef_instruction_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - base = get_python_undefined_instruction_type(); +CREATE_DYN_CONSTRUCTOR(undefined_instruction, G_TYPE_UNDEFINED_INSTRUCTION); - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_UNDEF_INSTRUCTION, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_undefined_instruction_init(PyObject *, PyObject *, PyObject *); - if (!status) - { - result = NULL; - goto exit; - } - } - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ +/* ------------------------ FONCTIONNALITES DE L'INSTRUCTION ------------------------ */ - simple_way: - result = PyType_GenericNew(type, args, kwds); +/* Indique le type de conséquences réél de l'instruction. */ +static PyObject *py_undefined_instruction_get_behavior(PyObject *, void *); - exit: - return result; -} +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** @@ -125,30 +75,27 @@ static PyObject *py_undef_instruction_new(PyTypeObject *type, PyObject *args, Py * * ******************************************************************************/ -static int py_undef_instruction_init(PyObject *self, PyObject *args, PyObject *kwds) +static int py_undefined_instruction_init(PyObject *self, PyObject *args, PyObject *kwds) { - unsigned long behavior; /* Conséquence pour l'instruct°*/ + InstrExpectedBehavior behavior; /* Conséquence pour l'instruct°*/ int ret; /* Bilan de lecture des args. */ - GUndefInstruction *instr; /* Instruction à manipuler */ - undef_extra_data_t *extra; /* Données insérées à modifier */ + GUndefinedInstruction *instr; /* Instruction à manipuler */ - static char *kwlist[] = { "behavior", NULL }; - -#define UNDEF_INSTRUCTION_DOC \ - "UndefInstruction represents all kinds of instructions which are" \ +#define UNDEFINED_INSTRUCTION_DOC \ + "UndefinedInstruction represents all kinds of instructions which are" \ " officially not part of a runnable instruction set.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " UndefInstruction(behavior)" \ + " UndefinedInstruction(behavior)" \ "\n" \ "Where behavior is a" \ - " pychrysalide.arch.instructions.UndefInstruction.ExpectedBehavior" \ + " pychrysalide.arch.instructions.UndefinedInstruction.ExpectedBehavior" \ " constant describing the state of the CPU once the instruction is run." /* Récupération des paramètres */ - ret = PyArg_ParseTupleAndKeywords(args, kwds, "k", kwlist, &behavior); + ret = PyArg_ParseTuple(args, "O&", convert_to_undefined_expected_behavior, &behavior); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -158,17 +105,22 @@ static int py_undef_instruction_init(PyObject *self, PyObject *args, PyObject *k /* Eléments de base */ - instr = G_UNDEF_INSTRUCTION(pygobject_get(self)); - - extra = GET_UNDEF_INSTR_EXTRA(instr); + instr = G_UNDEFINED_INSTRUCTION(pygobject_get(self)); - extra->behavior = behavior; + if (!g_undefined_instruction_create(instr, behavior)) + return -1; return 0; } + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES DE L'INSTRUCTION */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : self = classe représentant une instruction. * @@ -182,24 +134,25 @@ static int py_undef_instruction_init(PyObject *self, PyObject *args, PyObject *k * * ******************************************************************************/ -static PyObject *py_undef_instruction_get_behavior(PyObject *self, void *closure) +static PyObject *py_undefined_instruction_get_behavior(PyObject *self, void *closure) { PyObject *result; /* Conversion à retourner */ - GUndefInstruction *instr; /* Version native */ + GUndefinedInstruction *instr; /* Version native */ InstrExpectedBehavior behavior; /* Comportement attendu */ -#define UNDEF_INSTRUCTION_BEHAVIOR_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - behavior, py_undef_instruction, \ - "Consequence carried by the undefined instruction.\n" \ - "\n" \ - "The result is provided as a" \ - " pychrysalide.arch.instructions.UndefInstruction.ExpectedBehavior" \ - " constant." \ +#define UNDEFINED_INSTRUCTION_BEHAVIOR_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + behavior, py_undefined_instruction, \ + "Consequence carried by the undefined instruction.\n" \ + "\n" \ + "The result is provided as a" \ + " pychrysalide.arch.instructions.UndefinedInstruction.ExpectedBehavior" \ + " constant." \ ) - instr = G_UNDEF_INSTRUCTION(pygobject_get(self)); - behavior = g_undef_instruction_get_behavior(instr); + instr = G_UNDEFINED_INSTRUCTION(pygobject_get(self)); + + behavior = g_undefined_instruction_get_behavior(instr); result = cast_with_constants_group_from_type(get_python_undefined_instruction_type(), "ExpectedBehavior", behavior); @@ -228,7 +181,7 @@ PyTypeObject *get_python_undefined_instruction_type(void) }; static PyGetSetDef py_undefined_instruction_getseters[] = { - UNDEF_INSTRUCTION_BEHAVIOR_ATTRIB, + UNDEFINED_INSTRUCTION_BEHAVIOR_ATTRIB, { NULL } }; @@ -236,18 +189,18 @@ PyTypeObject *get_python_undefined_instruction_type(void) PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.arch.instructions.UndefInstruction", + .tp_name = "pychrysalide.arch.instructions.UndefinedInstruction", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = UNDEF_INSTRUCTION_DOC, + .tp_doc = UNDEFINED_INSTRUCTION_DOC, .tp_methods = py_undefined_instruction_methods, .tp_getset = py_undefined_instruction_getseters, - .tp_init = py_undef_instruction_init, - .tp_new = py_undef_instruction_new, + .tp_init = py_undefined_instruction_init, + .tp_new = py_undefined_instruction_new, }; @@ -260,7 +213,7 @@ PyTypeObject *get_python_undefined_instruction_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.....UndefInstruction'. * +* Description : Prend en charge l'objet '....UndefinedInstruction'. * * * * Retour : Bilan de l'opération. * * * @@ -285,7 +238,7 @@ bool ensure_python_undefined_instruction_is_registered(void) if (!ensure_python_arch_instruction_is_registered()) return false; - if (!register_class_for_pygobject(dict, G_TYPE_UNDEF_INSTRUCTION, type)) + if (!register_class_for_pygobject(dict, G_TYPE_UNDEFINED_INSTRUCTION, type)) return false; if (!define_undefined_instruction_constants(type)) @@ -329,7 +282,7 @@ int convert_to_undefined_instruction(PyObject *arg, void *dst) break; case 1: - *((GUndefInstruction **)dst) = G_UNDEF_INSTRUCTION(pygobject_get(arg)); + *((GUndefinedInstruction **)dst) = G_UNDEFINED_INSTRUCTION(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/arch/instructions/undefined.h b/plugins/pychrysalide/arch/instructions/undefined.h index 3fa0453..1453612 100644 --- a/plugins/pychrysalide/arch/instructions/undefined.h +++ b/plugins/pychrysalide/arch/instructions/undefined.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * undefined.h - prototypes pour l'équivalent Python du fichier "arch/instructions/undefined.h" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -34,7 +34,7 @@ /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_undefined_instruction_type(void); -/* Prend en charge l'objet 'pychrysalide.arch.instructions.UndefInstruction'. */ +/* Prend en charge l'objet 'pychrysalide.arch.instructions.UndefinedInstruction'. */ bool ensure_python_undefined_instruction_is_registered(void); /* Tente de convertir en instruction non définie. */ diff --git a/plugins/pychrysalide/arch/module-ui.c b/plugins/pychrysalide/arch/module-ui.c new file mode 100644 index 0000000..65d1290 --- /dev/null +++ b/plugins/pychrysalide/arch/module-ui.c @@ -0,0 +1,66 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire arch (forme graphique) en tant que module + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module-ui.h" + + +#include <assert.h> + + +#include "operand-ui.h" +#include "../glibext/generator.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'arch' (mode UI). * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_arch_module_ui(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_arch_operand_ui_is_registered(); + + /** + * Préparation du terrain pour les instructions, sans lien directe + * de la partie UI depuis la partie NOX. + */ + if (result) result = ensure_python_token_generator_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/pychrysalide/arch/module-ui.h b/plugins/pychrysalide/arch/module-ui.h new file mode 100644 index 0000000..afa31d2 --- /dev/null +++ b/plugins/pychrysalide/arch/module-ui.h @@ -0,0 +1,38 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire arch (forme graphique) en tant que module + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_ARCH_MODULE_UI_H +#define _PLUGINS_PYCHRYSALIDE_ARCH_MODULE_UI_H + + +#include <Python.h> +#include <stdbool.h> + + +/* Intègre les objets du module 'arch' (mode UI). */ +bool populate_arch_module_ui(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_ARCH_MODULE_UI_H */ diff --git a/plugins/pychrysalide/arch/module.c b/plugins/pychrysalide/arch/module.c index bbdaf76..94f5ad7 100644 --- a/plugins/pychrysalide/arch/module.c +++ b/plugins/pychrysalide/arch/module.c @@ -28,22 +28,17 @@ #include <assert.h> -#include <arch/archbase.h> - - /* #include "context.h" #include "instriter.h" +*/ #include "instruction.h" #include "operand.h" -#include "processor.h" +//#include "processor.h" #include "register.h" -*/ #include "vmpa.h" -/* #include "instructions/module.h" #include "operands/module.h" -*/ #include "../helpers.h" @@ -80,10 +75,8 @@ bool add_arch_module(PyObject *super) result = (module != NULL); - /* if (result) result = add_arch_instructions_module(module); if (result) result = add_arch_operands_module(module); - */ return result; @@ -111,18 +104,16 @@ bool populate_arch_module(void) /* if (result) result = ensure_python_proc_context_is_registered(); if (result) result = ensure_python_instr_iterator_is_registered(); + */ if (result) result = ensure_python_arch_instruction_is_registered(); if (result) result = ensure_python_arch_operand_is_registered(); - if (result) result = ensure_python_arch_processor_is_registered(); + //if (result) result = ensure_python_arch_processor_is_registered(); if (result) result = ensure_python_arch_register_is_registered(); - */ if (result) result = ensure_python_vmpa_is_registered(); if (result) result = ensure_python_mrange_is_registered(); - /* if (result) result = populate_arch_instructions_module(); if (result) result = populate_arch_operands_module(); - */ assert(result); diff --git a/plugins/pychrysalide/arch/operand-ui.c b/plugins/pychrysalide/arch/operand-ui.c new file mode 100644 index 0000000..5062513 --- /dev/null +++ b/plugins/pychrysalide/arch/operand-ui.c @@ -0,0 +1,461 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * operand-ui.c - équivalent Python du fichier "arch/operand-ui.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "operand-ui.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <arch/operand-ui-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void py_arch_operand_ui_interface_init(GArchOperandUIInterface *, gpointer *); + +/* Traduit un opérande en version humainement lisible. */ +static void py_arch_operand_ui_print_wrapper(const GArchOperandUI *, GBufferLine *); + +/* Construit un petit résumé concis de l'opérande. */ +static char *py_arch_operand_ui_build_tooltip_wrapper(const GArchOperandUI *, const GLoadedBinary *); + +/* Traduit un opérande en version humainement lisible. */ +static PyObject *py_arch_operand_ui_print(PyObject *, PyObject *); + +/* Construit un petit résumé concis de l'opérande. */ +static PyObject *py_arch_operand_ui_build_tooltip(PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_arch_operand_ui_interface_init(GArchOperandUIInterface *iface, gpointer *unused) +{ +#define ARCH_OPERAND_UI_DOC \ + "The ArchOperandUI interface ensure pychrysalide.arch.ArchOperand" \ + " implementations provide UI features when Chrysalide is running with" \ + " a GUI.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(ArchOperand, ArchOperandUi):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.arch.ArchOperandUI._print();\n" \ + "* pychrysalide.arch.ArchOperandUI._build_tooltip().\n" + + iface->print = py_arch_operand_ui_print_wrapper; + iface->build_tooltip = py_arch_operand_ui_build_tooltip_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : operand = registre visé par la procédure. * +* * +* Description : Traduit un opérande en version humainement lisible. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_arch_operand_ui_print_wrapper(const GArchOperandUI *operand, GBufferLine *line) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define ARCH_OPERAND_UI_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _print, "$self, line, /", \ + METH_VARARGS, \ + "Abstract method used to print an operand into a rendering" \ + " *line*, which is a provided pychrysalide.glibext.BufferLine" \ + " instance." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(operand)); + + if (has_python_method(pyobj, "_print")) + { + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); + + pyret = run_python_method(pyobj, "_print", args); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : operand = opérande à consulter. * +* binary = informations relatives au binaire chargé. * +* * +* Description : Construit un petit résumé concis de l'opérande. * +* * +* Retour : Chaîne de caractères à libérer après usage ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *py_arch_operand_ui_build_tooltip_wrapper(const GArchOperandUI *operand, const GLoadedBinary *binary) +{ + char *result; /* Description à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define ARCH_OPERAND_UI_BUILD_TOOLTIP_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _build_tooltip, "$self, binary, /", \ + METH_VARARGS, \ + "Abstract method used to build a tooltip text shown when the" \ + " mouse is over an operand.\n" \ + "\n" \ + "A pychrysalide.analysis.LoadedBinary instance is provided in" \ + " case of need." \ +) + + result = NULL; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(operand)); + + if (has_python_method(pyobj, "_build_tooltip")) + { + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(binary))); + + pyret = run_python_method(pyobj, "_build_tooltip", args); + + if (pyret != NULL) + { + if (PyUnicode_Check(pyret)) + result = strdup(PyUnicode_AsUTF8(pyret)); + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * +* * +* Description : Traduit un opérande en version humainement lisible. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_ui_print(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + +#if 0 // TODO + + GBufferLine *line; /* Zone d'impression du rendu */ + int ret; /* Bilan de lecture des args. */ + GArchOperandUI *operand; /* Mécanismes natifs */ + +#define ARCH_OPERAND_UI_PRINT_METHOD PYTHON_METHOD_DEF \ +( \ + print, "$self, line", \ + METH_VARARGS, py_arch_operand_ui, \ + "Translate an operand into a human readable version.\n" \ + "\n" \ + "The *line* arguement is a pychrysalide.glibext.BufferLine" \ + " instance which has to get filled with rendering" \ + " information.\n" \ + "\n" \ + "The result returns nothing (*None*)." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_buffer_line, &line); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND_UI(pygobject_get(self)); + + g_arch_operand_ui_print(operand, line); + +#endif + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * +* * +* Description : Construit un petit résumé concis de l'opérande. * +* * +* Retour : Chaîne de caractères à libérer après usage ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_ui_build_tooltip(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + +#if 0 // TODO + + GLoadedBinary *binary; /* Infos sur le binaire chargé */ + int ret; /* Bilan de lecture des args. */ + GArchOperandUI *operand; /* Mécanismes natifs */ + char *tooltip; /* Eventuelle indication */ + +#define ARCH_OPERAND_UI_BUILD_TOOLTIP_METHOD PYTHON_METHOD_DEF \ +( \ + build_tooltip, "$self, binary", \ + METH_VARARGS, py_arch_operand_ui, \ + "Build a tooltip text shown when the mouse is over an" \ + " operand.\n" \ + "\n" \ + "The *binary* argument is a pychrysalide.analysis.LoadedBinary" \ + " instance provided in case of need." \ + "\n" \ + "The result is a string or *None* in case of error." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_loaded_binary, &binary); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND_UI(pygobject_get(self)); + + tooltip = g_arch_operand_ui_build_tooltip(operand, binary); + + if (tooltip != NULL) + { + PyUnicode_FromString(tooltip); + free(tooltip); + } + else + +#endif + + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_arch_operand_ui_type(void) +{ + static PyMethodDef py_arch_operand_ui_methods[] = { + ARCH_OPERAND_UI_PRINT_WRAPPER, + ARCH_OPERAND_UI_BUILD_TOOLTIP_WRAPPER, +#if 0 // TODO + ARCH_OPERAND_UI_PRINT_METHOD, + ARCH_OPERAND_UI_BUILD_TOOLTIP_METHOD, +#endif + { NULL } + }; + + static PyGetSetDef py_arch_operand_ui_getseters[] = { + { NULL } + }; + + static PyTypeObject py_arch_operand_ui_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.arch.ArchOperandUI", + .tp_basicsize = sizeof(PyObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = ARCH_OPERAND_UI_DOC, + + .tp_methods = py_arch_operand_ui_methods, + .tp_getset = py_arch_operand_ui_getseters + + }; + + return &py_arch_operand_ui_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.arch.ArchOperandUI'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_arch_operand_ui_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'ArchOperandUI' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_arch_operand_ui_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_arch_operand_ui_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.arch"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_ARCH_OPERAND_UI, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'exportation graphique. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_arch_operand_ui(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_arch_operand_ui_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to UI arch operand"); + break; + + case 1: + *((GArchOperandUI **)dst) = G_ARCH_OPERAND_UI(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/arch/operand-ui.h b/plugins/pychrysalide/arch/operand-ui.h new file mode 100644 index 0000000..b9e2131 --- /dev/null +++ b/plugins/pychrysalide/arch/operand-ui.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * operand-ui.h - prototypes pour l'équivalent Python du fichier "arch/operand-ui.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_ARCH_OPERAND_UI_H +#define _PLUGINS_PYCHRYSALIDE_ARCH_OPERAND_UI_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_arch_operand_ui_type(void); + +/* Prend en charge l'objet 'pychrysalide.arch.ArchOperandUI'. */ +bool ensure_python_arch_operand_ui_is_registered(void); + +/* Tente de convertir en interface d'exportation graphique. */ +int convert_to_arch_operand_ui(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_ARCH_OPERAND_UI_H */ diff --git a/plugins/pychrysalide/arch/operand.c b/plugins/pychrysalide/arch/operand.c index 0aee4f7..2281dae 100644 --- a/plugins/pychrysalide/arch/operand.c +++ b/plugins/pychrysalide/arch/operand.c @@ -30,23 +30,32 @@ #include <i18n.h> #include <arch/operand-int.h> -#include <plugins/dt.h> +#include <glibext/strbuilder-int.h> #include "../access.h" #include "../helpers.h" +#include "../glibext/comparable.h" +#include "../glibext/hashable.h" +#include "../glibext/objhole.h" +#include "../glibext/serialize.h" #include "../glibext/singleton.h" +#include "../glibext/strbuilder.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_arch_operand_new(PyTypeObject *, PyObject *, PyObject *); - /* Initialise la classe générique des opérandes. */ -static void py_arch_operand_init_gclass(GArchOperandClass *, gpointer); +static int py_arch_operand_init_gclass(GArchOperandClass *, PyTypeObject *); + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(arch_operand, G_TYPE_ARCH_OPERAND); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_arch_operand_init(PyObject *, PyObject *, PyObject *); + +#if 0 /* Compare un opérande avec un autre. */ static int py_arch_operand___cmp___wrapper(const GArchOperand *, const GArchOperand *, bool); @@ -57,14 +66,6 @@ static char *py_arch_operand_find_inner_operand_path_wrapper(const GArchOperand /* Obtient l'opérande correspondant à un chemin donné. */ static GArchOperand *py_arch_operand_get_inner_operand_from_path_wrapper(const GArchOperand *, const char *); -/* Traduit un opérande en version humainement lisible. */ -static void py_arch_operand_print_wrapper(const GArchOperand *, GBufferLine *); - -#ifdef INCLUDE_GTK_SUPPORT - -/* Construit un petit résumé concis de l'opérande. */ -static char *py_arch_operand_build_tooltip_wrapper(const GArchOperand *, const GLoadedBinary *); - #endif @@ -72,6 +73,9 @@ static char *py_arch_operand_build_tooltip_wrapper(const GArchOperand *, const G /* ------------------------ DEFINITION D'OPERANDE QUELCONQUE ------------------------ */ +#if 0 + + /* Effectue une comparaison avec un objet Python 'ArchOperand'. */ static PyObject *py_arch_operand_richcompare(PyObject *, PyObject *, int); @@ -81,6 +85,19 @@ static PyObject *py_arch_operand_find_inner_operand_path(PyObject *, PyObject *) /* Obtient l'opérande correspondant à un chemin donné. */ static PyObject *py_arch_operand_get_inner_operand_from_path(PyObject *, PyObject *); +#endif + + +/* Ajoute une information complémentaire à un opérande. */ +static PyObject *py_arch_operand_set_flag(PyObject *, PyObject *); + +/* Retire une information complémentaire à un opérande. */ +static PyObject *py_arch_operand_unset_flag(PyObject *, PyObject *); + +/* Détermine si un opérande possède un fanion particulier. */ +static PyObject *py_arch_operand_has_flag(PyObject *, PyObject *); + + /* ---------------------------------------------------------------------------------- */ @@ -90,113 +107,109 @@ static PyObject *py_arch_operand_get_inner_operand_from_path(PyObject *, PyObjec /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * -* Description : Accompagne la création d'une instance dérivée en Python. * +* Description : Initialise la classe générique des opérandes. * * * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_operand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_arch_operand_init_gclass(GArchOperandClass *gclass, PyTypeObject *pyclass) { - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - -#define ARCH_OPERAND_DOC \ - "The ArchOperand object aims to get subclassed to create" \ - " operands of any kind for new architectures.\n" \ - "\n" \ - "Calls to the *__init__* constructor of this abstract object expect"\ - " no particular argument.\n" \ - "\n" \ - "The following methods have to be defined for new classes:\n" \ - "* pychrysalide.arch.ArchRegister.__cmp__();\n" \ - "* pychrysalide.arch.ArchRegister._print();\n" \ - "* pychrysalide.arch.ArchRegister._build_tooltip().\n" \ - "\n" \ - "Some extra method definitions are optional for new classes:\n" \ - "* pychrysalide.arch.ArchRegister._find_inner_operand_path();\n" \ - "* pychrysalide.arch.ArchRegister._get_inner_operand_from_path().\n"\ - "\n" \ - "Chrysalide creates an internal glue to provide rich comparisons" \ - " for operands based on the old-style *__cmp__* function." - /* Validations diverses */ +#if 0 + GStringBuilderInterface *iface; /* Interface utilisée */ - base = get_python_arch_operand_type(); + iface = g_type_interface_peek(gclass, G_TYPE_STRING_BUILDER); - if (type == base) - { - result = NULL; - PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); - goto exit; - } - /* Mise en place d'un type dédié */ + /* + printf("???????? init Python Operand ?????????????? -> class: %p '%s' - strbuilder iface: %p\n", + gclass, g_type_name(G_TYPE_FROM_CLASS(gclass)), iface); + */ - first_time = (g_type_from_name(type->tp_name) == 0); +#endif - gtype = build_dynamic_type(G_TYPE_ARCH_OPERAND, type->tp_name, - (GClassInitFunc)py_arch_operand_init_gclass, NULL, NULL); +#if 0 - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); + class->compare = py_arch_operand___cmp___wrapper; + class->find_inner = py_arch_operand_find_inner_operand_path_wrapper; + class->get_inner = py_arch_operand_get_inner_operand_from_path_wrapper; - if (!status) - { - result = NULL; - goto exit; - } - } +#endif - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - result = PyType_GenericNew(type, args, kwds); - exit: + //PY_CLASS_SET_WRAPPER(gclass->xxx, py_arch_operand_xxx_wrapper); - return result; + return 0; } /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Initialise la classe générique des opérandes. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : - * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static void py_arch_operand_init_gclass(GArchOperandClass *class, gpointer unused) +static int py_arch_operand_init(PyObject *self, PyObject *args, PyObject *kwds) { - class->compare = py_arch_operand___cmp___wrapper; - class->find_inner = py_arch_operand_find_inner_operand_path_wrapper; - class->get_inner = py_arch_operand_get_inner_operand_from_path_wrapper; + //unsigned int endianness; /* Boutisme du processeur */ + int ret; /* Bilan de lecture des args. */ + //GArchProcessor *proc; /* Processeur à manipuler */ - class->print = py_arch_operand_print_wrapper; -#ifdef INCLUDE_GTK_SUPPORT - class->build_tooltip = py_arch_operand_build_tooltip_wrapper; -#endif +#define ARCH_OPERAND_DOC \ + "The ArchOperand object aims to get subclassed to create" \ + " operands of any kind for new architectures.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect"\ + " no particular argument.\n" \ + "\n" \ + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.arch.ArchRegister.__cmp__();\n" \ + "* pychrysalide.arch.ArchRegister._print();\n" \ + "* pychrysalide.arch.ArchRegister._build_tooltip().\n" \ + "\n" \ + "Some extra method definitions are optional for new classes:\n" \ + "* pychrysalide.arch.ArchRegister._find_inner_operand_path();\n" \ + "* pychrysalide.arch.ArchRegister._get_inner_operand_from_path().\n"\ + "\n" \ + "Chrysalide creates an internal glue to provide rich comparisons" \ + " for operands based on the old-style *__cmp__* function." + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + //proc = G_ARCH_PROCESSOR(pygobject_get(self)); + + //proc->endianness = endianness; + + return 0; } + +#if 0 + /****************************************************************************** * * * Paramètres : a = premier opérande à consulter. * @@ -410,126 +423,6 @@ static GArchOperand *py_arch_operand_get_inner_operand_from_path_wrapper(const G } -/****************************************************************************** -* * -* Paramètres : operand = registre visé par la procédure. * -* * -* Description : Traduit un opérande en version humainement lisible. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_arch_operand_print_wrapper(const GArchOperand *operand, GBufferLine *line) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_OPERAND_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _print, "$self, line, /", \ - METH_VARARGS, \ - "Abstract method used to print the operand into a rendering" \ - " line, which is a provided pychrysalide.glibext.BufferLine" \ - " instance." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(operand)); - - if (has_python_method(pyobj, "_print")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); - - pyret = run_python_method(pyobj, "_print", args); - - Py_XDECREF(pyret); - - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -#ifdef INCLUDE_GTK_SUPPORT - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à consulter. * -* binary = informations relatives au binaire chargé. * -* * -* Description : Construit un petit résumé concis de l'opérande. * -* * -* Retour : Chaîne de caractères à libérer après usage ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *py_arch_operand_build_tooltip_wrapper(const GArchOperand *operand, const GLoadedBinary *binary) -{ - char *result; /* Description à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_OPERAND_BUILD_TOOLTIP_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _build_tooltip, "$self, line, /", \ - METH_VARARGS, \ - "Abstract method used to build a tooltip text shown when the" \ - " mouse is over the operand.\n" \ - "\n" \ - "A pychrysalide.analysis.LoadedBinary instance is provided in" \ - " case of need." \ -) - - result = NULL; - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(operand)); - - if (has_python_method(pyobj, "_build_tooltip")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(binary))); - - pyret = run_python_method(pyobj, "_build_tooltip", args); - - if (pyret != NULL) - { - if (PyUnicode_Check(pyret)) - result = strdup(PyUnicode_AsUTF8(pyret)); - } - - Py_XDECREF(pyret); - - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return result; - -} - #endif @@ -540,6 +433,9 @@ static char *py_arch_operand_build_tooltip_wrapper(const GArchOperand *operand, /* ---------------------------------------------------------------------------------- */ + +#if 0 + /****************************************************************************** * * * Paramètres : a = premier object Python à consulter. * @@ -701,6 +597,164 @@ static PyObject *py_arch_operand_get_inner_operand_from_path(PyObject *self, PyO } +#endif + + + + + + + + + + + +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Ajoute une information complémentaire à un opérande. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_set_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchOperand *operand; /* Opérande manipulé */ + bool status; /* Bilan à transmettre */ + +#define ARCH_OPERAND_SET_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + set_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_operand, \ + "Add some flags to the operand.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to apply to the operand state.\n" \ + "\n" \ + "The result is an boolean status: *True* for operation" \ + " success, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND(pygobject_get(self)); + + status = g_arch_operand_set_flag(operand, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Retire une information complémentaire à un opérande. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_unset_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchOperand *operand; /* Opérande manipulé */ + bool status; /* Bilan à transmettre */ + +#define ARCH_OPERAND_UNSET_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + unset_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_operand, \ + "Remove some flags from the operand.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to delete from the operand state.\n" \ + "\n" \ + "The result is an boolean status: *True* for operation" \ + " success, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND(pygobject_get(self)); + + status = g_arch_operand_unset_flag(operand, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Détermine si un opérande possède un fanion particulier. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_has_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchOperand *operand; /* Opérande manipulé */ + bool status; /* Bilan à transmettre */ + +#define ARCH_OPERAND_HAS_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + has_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_operand, \ + "Tell if some flags are set for the operand.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to test for the operand state.\n" \ + "\n" \ + "The result is an boolean status: *True* if the bits" \ + " are active, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND(pygobject_get(self)); + + status = g_arch_operand_has_flag(operand, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + /****************************************************************************** * * @@ -717,6 +771,11 @@ static PyObject *py_arch_operand_get_inner_operand_from_path(PyObject *self, PyO PyTypeObject *get_python_arch_operand_type(void) { static PyMethodDef py_arch_operand_methods[] = { + ARCH_OPERAND_SET_FLAG_METHOD, + ARCH_OPERAND_UNSET_FLAG_METHOD, + ARCH_OPERAND_HAS_FLAG_METHOD, + + /* ARCH_OPERAND_CMP_WRAPPER, ARCH_OPERAND_FIND_INNER_OPERAND_PATH_WRAPPER, ARCH_OPERAND_GET_INNER_OPERAND_FROM_PATH_WRAPPER, @@ -726,6 +785,7 @@ PyTypeObject *get_python_arch_operand_type(void) #endif ARCH_OPERAND_FIND_INNER_OPERAND_PATH_METHOD, ARCH_OPERAND_GET_INNER_OPERAND_FROM_PATH_METHOD, + */ { NULL } }; @@ -744,11 +804,12 @@ PyTypeObject *get_python_arch_operand_type(void) .tp_doc = ARCH_OPERAND_DOC, - .tp_richcompare = py_arch_operand_richcompare, + //.tp_richcompare = py_arch_operand_richcompare, .tp_methods = py_arch_operand_methods, .tp_getset = py_arch_operand_getseters, + .tp_init = py_arch_operand_init, .tp_new = py_arch_operand_new, }; @@ -784,9 +845,26 @@ bool ensure_python_arch_operand_is_registered(void) dict = PyModule_GetDict(module); + if (!ensure_python_thick_object_is_registered()) + return false; + + if (!ensure_python_comparable_object_is_registered()) + return false; + + if (!ensure_python_hashable_object_is_registered()) + return false; + + if (!ensure_python_serializable_object_is_registered()) + return false; + if (!ensure_python_singleton_candidate_is_registered()) return false; + if (!ensure_python_string_builder_is_registered()) + return false; + + pyg_register_class_init(G_TYPE_ARCH_OPERAND, (PyGClassInitFunc)py_arch_operand_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_ARCH_OPERAND, type)) return false; diff --git a/plugins/pychrysalide/arch/operand.h b/plugins/pychrysalide/arch/operand.h index 9cb40a0..f3bfbf2 100644 --- a/plugins/pychrysalide/arch/operand.h +++ b/plugins/pychrysalide/arch/operand.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * operand.h - prototypes pour l'équivalent Python du fichier "arch/operand.h" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -31,9 +31,6 @@ -/* ------------------------ DEFINITION D'OPERANDE QUELCONQUE ------------------------ */ - - /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_arch_operand_type(void); diff --git a/plugins/pychrysalide/arch/operands/Makefile.am b/plugins/pychrysalide/arch/operands/Makefile.am index a41cbbb..3b753cc 100644 --- a/plugins/pychrysalide/arch/operands/Makefile.am +++ b/plugins/pychrysalide/arch/operands/Makefile.am @@ -1,19 +1,21 @@ noinst_LTLIBRARIES = libpychrysaarchoperands.la +# libpychrysaarchoperands_la_SOURCES = \ +# feeder.h feeder.c \ +# proxy.h proxy.c \ +# rename.h rename.c \ +# target.h target.c \ +# targetable.h targetable.c + libpychrysaarchoperands_la_SOURCES = \ constants.h constants.c \ - feeder.h feeder.c \ immediate.h immediate.c \ known.h known.c \ module.h module.c \ - proxy.h proxy.c \ - register.h register.c \ - rename.h rename.c \ - target.h target.c \ - targetable.h targetable.c + register.h register.c -libpychrysaarchoperands_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpychrysaarchoperands_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/arch/operands/constants.c b/plugins/pychrysalide/arch/operands/constants.c index b9d80e4..78eeded 100644 --- a/plugins/pychrysalide/arch/operands/constants.c +++ b/plugins/pychrysalide/arch/operands/constants.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * constants.c - ajout des constantes de base pour les opérandes * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -47,20 +47,33 @@ * * ******************************************************************************/ -bool define_imm_operand_constants(PyTypeObject *type) +bool define_immediate_operand_constants(PyTypeObject *type) { bool result; /* Bilan à retourner */ PyObject *values; /* Groupe de valeurs à établir */ values = PyDict_New(); + result = add_const_to_group(values, "ZERO_PADDING_BY_DEFAULT", IOF_ZERO_PADDING_BY_DEFAULT); + if (result) result = add_const_to_group(values, "ZERO_PADDING", IOF_ZERO_PADDING); + + if (!result) + { + Py_DECREF(values); + goto exit; + } + + result = attach_constants_group_to_type(type, true, "ImmOperandFlag", values, + "Specific state bits for immediate operands."); + + values = PyDict_New(); + result = add_const_to_group(values, "BIN", IOD_BIN); if (result) result = add_const_to_group(values, "OCT", IOD_OCT); if (result) result = add_const_to_group(values, "DEC", IOD_DEC); if (result) result = add_const_to_group(values, "HEX", IOD_HEX); if (result) result = add_const_to_group(values, "CHAR", IOD_CHAR); if (result) result = add_const_to_group(values, "COUNT", IOD_COUNT); - if (result) result = add_const_to_group(values, "LAST_VALID", IOD_LAST_VALID); if (!result) { diff --git a/plugins/pychrysalide/arch/operands/constants.h b/plugins/pychrysalide/arch/operands/constants.h index 71a26cc..5170faa 100644 --- a/plugins/pychrysalide/arch/operands/constants.h +++ b/plugins/pychrysalide/arch/operands/constants.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * constants.h - prototypes pour l'ajout des constantes de base pour les opérandes * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,11 +32,14 @@ /* Définit les constantes relatives aux opérandes d'immédiats. */ -bool define_imm_operand_constants(PyTypeObject *); +bool define_immediate_operand_constants(PyTypeObject *); /* Tente de convertir en constante ImmOperandDisplay. */ int convert_to_imm_operand_display(PyObject *, void *); +#define cast_imm_operand_display_to_python(v) \ + cast_with_constants_group_from_type(get_python_immediate_operand_type(), "ImmOperandDisplay", v) + #endif /* _PLUGINS_PYCHRYSALIDE_ARCH_OPERANDS_CONSTANTS_H */ diff --git a/plugins/pychrysalide/arch/operands/immediate.c b/plugins/pychrysalide/arch/operands/immediate.c index 2239eb2..a335db3 100644 --- a/plugins/pychrysalide/arch/operands/immediate.c +++ b/plugins/pychrysalide/arch/operands/immediate.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * immediate.c - équivalent Python du fichier "arch/operands/immediate.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -30,201 +30,119 @@ #include <i18n.h> - - -#include <arch/operands/immediate.h> +#include <arch/operands/immediate-int.h> #include "constants.h" -#include "rename.h" -#include "targetable.h" #include "../operand.h" #include "../../access.h" +#include "../../constants.h" #include "../../helpers.h" #include "../../analysis/content.h" -#include "../../glibext/bufferline.h" -/* Crée un nouvel objet Python de type 'ImmOperand'. */ -static PyObject *py_imm_operand_new(PyTypeObject *, PyObject *, PyObject *); +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(immediate_operand, G_TYPE_IMMEDIATE_OPERAND); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_immediate_operand_init(PyObject *, PyObject *, PyObject *); + + -/* Compare un opérande avec un autre. */ -static PyObject *py_imm_operand___cmp__(PyObject *, PyObject *); +/* ---------------------------- DEFINITION D'UN IMMEDIAT ---------------------------- */ -/* Traduit un opérande en version humainement lisible. */ -static PyObject *py_imm_operand__print(PyObject *, PyObject *); /* Renseigne la taille de la valeur indiquée à la construction. */ -static PyObject *py_imm_operand_get_size(PyObject *, void *); +static PyObject *py_immediate_operand_get_size(PyObject *, void *); /* Fournit la valeur portée par une opérande numérique. */ -static PyObject *py_imm_operand_get_value(PyObject *, void *); +static PyObject *py_immediate_operand_get_value(PyObject *, void *); + +/* Indique le signe d'une valeur immédiate. */ +static PyObject *py_immediate_operand_is_negative(PyObject *, void *); /* Indique le format textuel par défaut de la valeur. */ -static PyObject *py_imm_operand_get_default_display(PyObject *, void *); +static PyObject *py_immediate_operand_get_default_display(PyObject *, void *); /* Définit le format textuel par défaut de la valeur. */ -static int py_imm_operand_set_default_display(PyObject *, PyObject *, void *); +static int py_immediate_operand_set_default_display(PyObject *, PyObject *, void *); /* Indique la grande ligne du format textuel de la valeur. */ -static PyObject *py_imm_operand_get_display(PyObject *, void *); +static PyObject *py_immediate_operand_get_display(PyObject *, void *); /* Définit la grande ligne du format textuel de la valeur. */ -static int py_imm_operand_set_display(PyObject *, PyObject *, void *); +static int py_immediate_operand_set_display(PyObject *, PyObject *, void *); + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + /****************************************************************************** * * -* Paramètres : type = type de l'objet à instancier. * +* Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * * * -* Description : Crée un nouvel objet Python de type 'ImmOperand'. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : Instance Python mise en place. * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_imm_operand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_immediate_operand_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *result; /* Instance à retourner */ - unsigned int raw_size; /* Taille obtenue de Python */ + MemoryDataSize size; /* Taille des données finale */ unsigned long long value; /* Valeur brute à représenter */ int ret; /* Bilan de lecture des args. */ - MemoryDataSize size; /* Taille des données finale */ - GArchOperand *operand; /* Création GLib à transmettre */ + GImmediateOperand *operand; /* Opérande natif à manipuler */ -#define IMM_OPERAND_DOC \ - "The ImmOperand deals with immediate value as operand." \ +#define IMMEDIATE_OPERAND_DOC \ + "The ImmediateOperand deals with immediate value as operand." \ "\n" \ "There are several ways to display these values in a disassembly," \ " the operand handles that.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " ImmOperand(size, value)" \ + " ImmediateOperand(size, value)" \ "\n" \ - "Where size specifies the original size of the provided value, as" \ - " a pychrysalide.analysis.BinContent.MemoryDataSize." + "Where *size* specifies the original size of the provided *value*," \ + " as a pychrysalide.MemoryDataSize." - ret = PyArg_ParseTuple(args, "IK", &raw_size, &value); - if (!ret) return NULL; + /* Récupération des paramètres */ - size = raw_size; + ret = PyArg_ParseTuple(args, "O&K", convert_to_memory_data_size, &size, &value); + if (!ret) return -1; - if (size != MDS_UNDEFINED - && !(MDS_4_BITS_UNSIGNED <= size && size <= MDS_64_BITS_UNSIGNED) - && !(MDS_4_BITS_SIGNED <= size && size <= MDS_64_BITS_SIGNED)) - { - PyErr_SetString(PyExc_ValueError, _("Invalid size to build an immediate operand")); - return NULL; - } + /* Initialisation d'un objet GLib */ - operand = g_imm_operand_new_from_value(size, value); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - result = pygobject_new(G_OBJECT(operand)); + /* Eléments de base */ - g_object_unref(operand); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - return (PyObject *)result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = serveur à manipuler. * -* args = arguments associés à l'appel. * -* * -* Description : Compare un opérande avec un autre. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_imm_operand___cmp__(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GImmOperand *other; /* Autre opérande à manipuler */ - int ret; /* Bilan de lecture des args. */ - GImmOperand *operand; /* Elément à manipuler */ - int status; /* Bilan de comparaison */ - -#define IMM_OPERAND_CMP_METHOD PYTHON_METHOD_DEF \ -( \ - __cmp__, "$self, other, /", \ - METH_VARARGS, py_imm_operand, \ - "Implementation of the required method used to compare the" \ - " operand with another one. This second object is always" \ - " an pychrysalide.arch.ImmOperand instance.\n" \ - "\n" \ - "See the parent class for more information about this method." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_imm_operand, &other); - if (!ret) return NULL; - - operand = G_IMM_OPERAND(pygobject_get(self)); - - status = g_arch_operand_compare(G_ARCH_OPERAND(operand), G_ARCH_OPERAND(other)); - - result = PyLong_FromLong(status); + if (!g_immediate_operand_create_from_value(operand, size, value)) + return -1; - return result; + return 0; } -/****************************************************************************** -* * -* Paramètres : self = serveur à manipuler. * -* args = arguments associés à l'appel. * -* * -* Description : Traduit un opérande en version humainement lisible. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ -static PyObject *py_imm_operand__print(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GBufferLine *line; /* Ligne fournie à peupler */ - int ret; /* Bilan de lecture des args. */ - GImmOperand *operand; /* Elément à manipuler */ - -#define IMM_OPERAND_PRINT_METHOD PYTHON_METHOD_DEF \ -( \ - _print, "$self, line, /", \ - METH_VARARGS, py_imm_operand, \ - "Implementation of the required method used to print the operand" \ - " into a rendering line, which is a provided" \ - " pychrysalide.glibext.BufferLine instance.\n" \ - "\n" \ - "See the parent class for more information about this method." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_buffer_line, &line); - if (!ret) return NULL; - - operand = G_IMM_OPERAND(pygobject_get(self)); - - g_arch_operand_print(G_ARCH_OPERAND(operand), line); - - result = Py_None; - Py_INCREF(result); - - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UN IMMEDIAT */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** @@ -240,25 +158,26 @@ static PyObject *py_imm_operand__print(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_imm_operand_get_size(PyObject *self, void *closure) +static PyObject *py_immediate_operand_get_size(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ MemoryDataSize size; /* Type de donnée représentée */ -#define IMM_OPERAND_SIZE_ATTRIB PYTHON_GET_DEF_FULL \ +#define IMMEDIATE_OPERAND_SIZE_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - size, py_imm_operand, \ + size, py_immediate_operand, \ "Get or set the size of the value contained in the operand." \ "\n" \ "The property is a value of type" \ - " pychrysalide.analysis.BinContent.MemoryDataSize." \ + " pychrysalide.MemoryDataSize." \ ) - operand = G_IMM_OPERAND(pygobject_get(self)); - size = g_imm_operand_get_size(operand); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); + + size = g_immediate_operand_get_size(operand); - result = cast_with_constants_group_from_type(get_python_binary_content_type(), "MemoryDataSize", size); + result = cast_memory_data_size_to_python(size); return result; @@ -278,10 +197,10 @@ static PyObject *py_imm_operand_get_size(PyObject *self, void *closure) * * ******************************************************************************/ -static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) +static PyObject *py_immediate_operand_get_value(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ MemoryDataSize size; /* Type de donnée représentée */ uint8_t uval8; /* Valeur sur 8 bits */ uint16_t uval16; /* Valeur sur 16 bits */ @@ -292,15 +211,15 @@ static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) int32_t sval32; /* Valeur sur 32 bits */ int64_t sval64; /* Valeur sur 64 bits */ -#define IMM_OPERAND_VALUE_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - value, py_imm_operand, \ - "Value of the immediate operand, as an integer." \ +#define IMMEDIATE_OPERAND_VALUE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + value, py_immediate_operand, \ + "Value of the immediate operand, as an integer." \ ) - operand = G_IMM_OPERAND(pygobject_get(self)); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - size = g_imm_operand_get_size(operand); + size = g_immediate_operand_get_size(operand); switch (size) { @@ -311,36 +230,36 @@ static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) break; case MDS_4_BITS_UNSIGNED: case MDS_8_BITS_UNSIGNED: - g_imm_operand_get_value(operand, size, &uval8); + g_immediate_operand_get_value(operand, size, &uval8); result = PyLong_FromUnsignedLong(uval8); break; case MDS_16_BITS_UNSIGNED: - g_imm_operand_get_value(operand, size, &uval16); + g_immediate_operand_get_value(operand, size, &uval16); result = PyLong_FromUnsignedLong(uval16); break; case MDS_32_BITS_UNSIGNED: - g_imm_operand_get_value(operand, size, &uval32); + g_immediate_operand_get_value(operand, size, &uval32); result = PyLong_FromUnsignedLong(uval32); break; case MDS_64_BITS_UNSIGNED: - g_imm_operand_get_value(operand, size, &uval64); + g_immediate_operand_get_value(operand, size, &uval64); result = PyLong_FromUnsignedLongLong(uval64); break; case MDS_4_BITS_SIGNED: case MDS_8_BITS_SIGNED: - g_imm_operand_get_value(operand, size, &sval8); + g_immediate_operand_get_value(operand, size, &sval8); result = PyLong_FromLong(sval8); break; case MDS_16_BITS_SIGNED: - g_imm_operand_get_value(operand, size, &sval16); + g_immediate_operand_get_value(operand, size, &sval16); result = PyLong_FromLong(sval16); break; case MDS_32_BITS_SIGNED: - g_imm_operand_get_value(operand, size, &sval32); + g_immediate_operand_get_value(operand, size, &sval32); result = PyLong_FromLong(sval32); break; case MDS_64_BITS_SIGNED: - g_imm_operand_get_value(operand, size, &sval64); + g_immediate_operand_get_value(operand, size, &sval64); result = PyLong_FromLongLong(sval64); break; @@ -363,6 +282,43 @@ static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * +* Description : Indique le signe d'une valeur immédiate. * +* * +* Retour : True si la valeur est strictement négative, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_immediate_operand_is_negative(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ + bool status; /* Etat à faire connaître */ + +#define IMMEDIATE_OPERAND_NEGATIVE_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + negative, py_immediate_operand, \ + "Sign of the value, as a boolean status." \ +) + + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); + + status = g_immediate_operand_is_negative(operand); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * * Description : Indique le format textuel par défaut de la valeur. * * * * Retour : Format global d'un affichage de valeur. * @@ -371,25 +327,26 @@ static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) * * ******************************************************************************/ -static PyObject *py_imm_operand_get_default_display(PyObject *self, void *closure) +static PyObject *py_immediate_operand_get_default_display(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ ImmOperandDisplay display; /* Type d'affichage courant */ -#define IMM_OPERAND_DEFAULT_DISPLAY_ATTRIB PYTHON_GETSET_DEF_FULL \ +#define IMMEDIATE_OPERAND_DEFAULT_DISPLAY_ATTRIB PYTHON_GETSET_DEF_FULL \ ( \ - default_display, py_imm_operand, \ + default_display, py_immediate_operand, \ "Define of the immediate operand default textual representation." \ "\n" \ "The property is a value of type" \ " pychrysalide.arch.operands.ImmOperand.ImmOperandDisplay." \ ) - operand = G_IMM_OPERAND(pygobject_get(self)); - display = g_imm_operand_get_default_display(operand); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); + + display = g_immediate_operand_get_default_display(operand); - result = cast_with_constants_group_from_type(get_python_imm_operand_type(), "ImmOperandDisplay", display); + result = cast_imm_operand_display_to_python(display); return result; @@ -410,10 +367,10 @@ static PyObject *py_imm_operand_get_default_display(PyObject *self, void *closur * * ******************************************************************************/ -static int py_imm_operand_set_default_display(PyObject *self, PyObject *value, void *closure) +static int py_immediate_operand_set_default_display(PyObject *self, PyObject *value, void *closure) { ImmOperandDisplay display; /* Type d'affichage demandé */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ if (!PyLong_Check(value)) { @@ -429,9 +386,9 @@ static int py_imm_operand_set_default_display(PyObject *self, PyObject *value, v return -1; } - operand = G_IMM_OPERAND(pygobject_get(self)); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - g_imm_operand_set_default_display(operand, display); + g_immediate_operand_set_default_display(operand, display); return 0; @@ -451,25 +408,27 @@ static int py_imm_operand_set_default_display(PyObject *self, PyObject *value, v * * ******************************************************************************/ -static PyObject *py_imm_operand_get_display(PyObject *self, void *closure) +static PyObject *py_immediate_operand_get_display(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ ImmOperandDisplay display; /* Type d'affichage courant */ -#define IMM_OPERAND_DISPLAY_ATTRIB PYTHON_GETSET_DEF_FULL \ -( \ - display, py_imm_operand, \ - "Define of the immediate operand current textual representation." \ - "\n" \ - "The property is a value of type" \ - " pychrysalide.arch.operands.ImmOperand.ImmOperandDisplay." \ +#define IMMEDIATE_OPERAND_DISPLAY_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + display, py_immediate_operand, \ + "Retrieve or define of the immediate operand current textual" \ + " representation." \ + "\n" \ + "The property is a value of type" \ + " pychrysalide.arch.operands.ImmOperand.ImmOperandDisplay." \ ) - operand = G_IMM_OPERAND(pygobject_get(self)); - display = g_imm_operand_get_display(operand); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - result = cast_with_constants_group_from_type(get_python_imm_operand_type(), "ImmOperandDisplay", display); + display = g_immediate_operand_get_display(operand); + + result = cast_imm_operand_display_to_python(display); return result; @@ -490,10 +449,10 @@ static PyObject *py_imm_operand_get_display(PyObject *self, void *closure) * * ******************************************************************************/ -static int py_imm_operand_set_display(PyObject *self, PyObject *value, void *closure) +static int py_immediate_operand_set_display(PyObject *self, PyObject *value, void *closure) { ImmOperandDisplay display; /* Type d'affichage demandé */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ if (!PyLong_Check(value)) { @@ -509,9 +468,9 @@ static int py_imm_operand_set_display(PyObject *self, PyObject *value, void *clo return -1; } - operand = G_IMM_OPERAND(pygobject_get(self)); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - g_imm_operand_set_display(operand, display); + g_immediate_operand_set_display(operand, display); return 0; @@ -530,40 +489,41 @@ static int py_imm_operand_set_display(PyObject *self, PyObject *value, void *clo * * ******************************************************************************/ -PyTypeObject *get_python_imm_operand_type(void) +PyTypeObject *get_python_immediate_operand_type(void) { - static PyMethodDef py_imm_operand_methods[] = { - IMM_OPERAND_CMP_METHOD, - IMM_OPERAND_PRINT_METHOD, + static PyMethodDef py_immediate_operand_methods[] = { { NULL } }; - static PyGetSetDef py_imm_operand_getseters[] = { - IMM_OPERAND_SIZE_ATTRIB, - IMM_OPERAND_VALUE_ATTRIB, - IMM_OPERAND_DEFAULT_DISPLAY_ATTRIB, - IMM_OPERAND_DISPLAY_ATTRIB, + static PyGetSetDef py_immediate_operand_getseters[] = { + IMMEDIATE_OPERAND_SIZE_ATTRIB, + IMMEDIATE_OPERAND_VALUE_ATTRIB, + IMMEDIATE_OPERAND_NEGATIVE_ATTRIB, + IMMEDIATE_OPERAND_DEFAULT_DISPLAY_ATTRIB, + IMMEDIATE_OPERAND_DISPLAY_ATTRIB, { NULL } }; - static PyTypeObject py_imm_operand_type = { + static PyTypeObject py_immediate_operand_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.arch.operands.ImmOperand", + .tp_name = "pychrysalide.arch.operands.ImmediateOperand", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = IMM_OPERAND_DOC, + .tp_doc = IMMEDIATE_OPERAND_DOC, + + .tp_methods = py_immediate_operand_methods, + .tp_getset = py_immediate_operand_getseters, - .tp_methods = py_imm_operand_methods, - .tp_getset = py_imm_operand_getseters, - .tp_new = py_imm_operand_new + .tp_init = py_immediate_operand_init, + .tp_new = py_immediate_operand_new, }; - return &py_imm_operand_type; + return &py_immediate_operand_type; } @@ -580,13 +540,13 @@ PyTypeObject *get_python_imm_operand_type(void) * * ******************************************************************************/ -bool ensure_python_imm_operand_is_registered(void) +bool ensure_python_immediate_operand_is_registered(void) { - PyTypeObject *type; /* Type Python 'ImmOperand' */ + PyTypeObject *type; /* Type 'ImmediateOperand' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ - type = get_python_imm_operand_type(); + type = get_python_immediate_operand_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -597,16 +557,10 @@ bool ensure_python_imm_operand_is_registered(void) if (!ensure_python_arch_operand_is_registered()) return false; - if (!ensure_python_targetable_operand_is_registered()) - return false; - - if (!ensure_python_renameable_operand_is_registered()) - return false; - - if (!register_class_for_pygobject(dict, G_TYPE_IMM_OPERAND, type)) + if (!register_class_for_pygobject(dict, G_TYPE_IMMEDIATE_OPERAND, type)) return false; - if (!define_imm_operand_constants(type)) + if (!define_immediate_operand_constants(type)) return false; } @@ -629,11 +583,11 @@ bool ensure_python_imm_operand_is_registered(void) * * ******************************************************************************/ -int convert_to_imm_operand(PyObject *arg, void *dst) +int convert_to_immediate_operand(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_imm_operand_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_immediate_operand_type()); switch (result) { @@ -647,7 +601,7 @@ int convert_to_imm_operand(PyObject *arg, void *dst) break; case 1: - *((GImmOperand **)dst) = G_IMM_OPERAND(pygobject_get(arg)); + *((GImmediateOperand **)dst) = G_IMMEDIATE_OPERAND(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/arch/operands/immediate.h b/plugins/pychrysalide/arch/operands/immediate.h index 4a1e6de..8b8de83 100644 --- a/plugins/pychrysalide/arch/operands/immediate.h +++ b/plugins/pychrysalide/arch/operands/immediate.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * immediate.h - prototypes pour l'équivalent Python du fichier "arch/operands/immediate.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,13 +32,13 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_imm_operand_type(void); +PyTypeObject *get_python_immediate_operand_type(void); -/* Prend en charge l'objet 'pychrysalide.arch.ImmOperand'. */ -bool ensure_python_imm_operand_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.arch.ImmediateOperand'. */ +bool ensure_python_immediate_operand_is_registered(void); /* Tente de convertir en opérande de valeur immédiate. */ -int convert_to_imm_operand(PyObject *, void *); +int convert_to_immediate_operand(PyObject *, void *); diff --git a/plugins/pychrysalide/arch/operands/known.c b/plugins/pychrysalide/arch/operands/known.c index fab426e..85cabc2 100644 --- a/plugins/pychrysalide/arch/operands/known.c +++ b/plugins/pychrysalide/arch/operands/known.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * known.c - équivalent Python du fichier "arch/operands/known.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -29,70 +29,92 @@ #include <pygobject.h> -#include <arch/operands/known.h> +#include <arch/operands/known-int.h> #include "immediate.h" -#include "rename.h" #include "../../access.h" #include "../../helpers.h" -/* Crée un nouvel objet Python de type 'KnownImmOperand'. */ -static PyObject *py_known_imm_operand_new(PyTypeObject *, PyObject *, PyObject *); +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ +CREATE_DYN_CONSTRUCTOR(known_immediate_operand, G_TYPE_KNOWN_IMMEDIATE_OPERAND); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_known_immediate_operand_init(PyObject *, PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + /****************************************************************************** * * -* Paramètres : type = type de l'objet à instancier. * +* Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * * * -* Description : Crée un nouvel objet Python de type 'KnownImmOperand'. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : Instance Python mise en place. * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_known_imm_operand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_known_immediate_operand_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *result; /* Instance à retourner */ - GImmOperand *imm; /* Opérande à remplacer */ + GImmediateOperand *imm; /* Opérande à remplacer */ const char *alt; /* Impression alternative */ int ret; /* Bilan de lecture des args. */ - GArchOperand *operand; /* Création GLib à transmettre */ - -#define KNOWN_IMM_OPERAND_DOC \ - "The KnownImmOperand provides replacement of" \ - " pychrysalide.arch.operands.ImmOperand instances by an alternative" \ - " text.\n" \ - "\n" \ - "Instances can be created using the following constructor:\n" \ - "\n" \ - " KnownImmOperand(imm, alt)" \ - "\n" \ - "Where imm is an operand of type pychrysalide.arch.operands.ImmOperand" \ - " and alt is a string providing the text to be rendered at object" \ + GKnownImmediateOperand *operand; /* Opérande natif à manipuler */ + +#define KNOWN_IMMEDIATE_OPERAND_DOC \ + "The KnownImmediateOperand provides replacement of" \ + " pychrysalide.arch.operands.ImmediateOperand instances by an alternative" \ + " text.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " KnownImmediateOperand(imm, alt)" \ + "\n" \ + "Where *imm* is an operand of type pychrysalide.arch.operands.ImmediateOperand" \ + " and *alt* is a string providing the text to be rendered at object" \ " display." - ret = PyArg_ParseTuple(args, "O&s", convert_to_imm_operand, &imm, &alt); - if (!ret) return NULL; + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&s", convert_to_immediate_operand, &imm, &alt); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ - operand = g_known_imm_operand_new(imm, alt); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - result = pygobject_new(G_OBJECT(operand)); + /* Eléments de base */ - g_object_unref(operand); + operand = G_KNOWN_IMMEDIATE_OPERAND(pygobject_get(self)); - return (PyObject *)result; + if (!g_known_immediate_operand_create(operand, imm, alt)) + return -1; + + return 0; } + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UN IMMEDIAT CONNU */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : - * @@ -105,34 +127,36 @@ static PyObject *py_known_imm_operand_new(PyTypeObject *type, PyObject *args, Py * * ******************************************************************************/ -PyTypeObject *get_python_known_imm_operand_type(void) +PyTypeObject *get_python_known_immediate_operand_type(void) { - static PyMethodDef py_known_imm_operand_methods[] = { + static PyMethodDef py_known_immediate_operand_methods[] = { { NULL } }; - static PyGetSetDef py_known_imm_operand_getseters[] = { + static PyGetSetDef py_known_immediate_operand_getseters[] = { { NULL } }; - static PyTypeObject py_known_imm_operand_type = { + static PyTypeObject py_known_immediate_operand_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.arch.operands.KnownImmOperand", + .tp_name = "pychrysalide.arch.operands.KnownImmediateOperand", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = KNOWN_IMM_OPERAND_DOC, + .tp_doc = KNOWN_IMMEDIATE_OPERAND_DOC, - .tp_methods = py_known_imm_operand_methods, - .tp_getset = py_known_imm_operand_getseters, - .tp_new = py_known_imm_operand_new + .tp_methods = py_known_immediate_operand_methods, + .tp_getset = py_known_immediate_operand_getseters, + + .tp_init = py_known_immediate_operand_init, + .tp_new = py_known_immediate_operand_new, }; - return &py_known_imm_operand_type; + return &py_known_immediate_operand_type; } @@ -141,7 +165,7 @@ PyTypeObject *get_python_known_imm_operand_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.arch.KnownImmOperand'. * +* Description : Prend en charge l'objet '....KnownImmediateOperand'. * * * * Retour : Bilan de l'opération. * * * @@ -149,13 +173,13 @@ PyTypeObject *get_python_known_imm_operand_type(void) * * ******************************************************************************/ -bool ensure_python_known_imm_operand_is_registered(void) +bool ensure_python_known_immediate_operand_is_registered(void) { - PyTypeObject *type; /* Type Python 'ImmOperand' */ + PyTypeObject *type; /* Type 'KnownImmediateOperand'*/ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ - type = get_python_known_imm_operand_type(); + type = get_python_known_immediate_operand_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -163,13 +187,10 @@ bool ensure_python_known_imm_operand_is_registered(void) dict = PyModule_GetDict(module); - if (!ensure_python_imm_operand_is_registered()) - return false; - - if (!ensure_python_renamed_operand_is_registered()) + if (!ensure_python_immediate_operand_is_registered()) return false; - if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_IMM_OPERAND, type)) + if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_IMMEDIATE_OPERAND, type)) return false; } @@ -192,11 +213,11 @@ bool ensure_python_known_imm_operand_is_registered(void) * * ******************************************************************************/ -int convert_to_known_imm_operand(PyObject *arg, void *dst) +int convert_to_known_immediate_operand(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_known_imm_operand_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_known_immediate_operand_type()); switch (result) { @@ -210,7 +231,7 @@ int convert_to_known_imm_operand(PyObject *arg, void *dst) break; case 1: - *((GKnownImmOperand **)dst) = G_KNOWN_IMM_OPERAND(pygobject_get(arg)); + *((GKnownImmediateOperand **)dst) = G_KNOWN_IMMEDIATE_OPERAND(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/arch/operands/known.h b/plugins/pychrysalide/arch/operands/known.h index b5ced68..f5b80e8 100644 --- a/plugins/pychrysalide/arch/operands/known.h +++ b/plugins/pychrysalide/arch/operands/known.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * known.h - prototypes pour l'équivalent Python du fichier "arch/operands/known.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,13 +32,13 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_known_imm_operand_type(void); +PyTypeObject *get_python_known_immediate_operand_type(void); -/* Prend en charge l'objet 'pychrysalide.arch.KnownImmOperand'. */ -bool ensure_python_known_imm_operand_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.arch.KnownImmediateOperand'. */ +bool ensure_python_known_immediate_operand_is_registered(void); /* Tente de convertir en remplaçant d'opérande d'immédiat. */ -int convert_to_known_imm_operand(PyObject *, void *); +int convert_to_known_immediate_operand(PyObject *, void *); diff --git a/plugins/pychrysalide/arch/operands/module.c b/plugins/pychrysalide/arch/operands/module.c index 89adecc..486e259 100644 --- a/plugins/pychrysalide/arch/operands/module.c +++ b/plugins/pychrysalide/arch/operands/module.c @@ -28,14 +28,20 @@ #include <assert.h> +/* #include "feeder.h" +*/ #include "immediate.h" #include "known.h" +/* #include "proxy.h" +*/ #include "register.h" +/* #include "rename.h" #include "target.h" #include "targetable.h" +*/ #include "../../helpers.h" @@ -101,15 +107,21 @@ bool populate_arch_operands_module(void) result = true; + /* if (result) result = ensure_python_proxy_feeder_is_registered(); - if (result) result = ensure_python_imm_operand_is_registered(); - if (result) result = ensure_python_known_imm_operand_is_registered(); + */ + if (result) result = ensure_python_immediate_operand_is_registered(); + if (result) result = ensure_python_known_immediate_operand_is_registered(); + /* if (result) result = ensure_python_proxy_operand_is_registered(); + */ if (result) result = ensure_python_register_operand_is_registered(); + /* if (result) result = ensure_python_renamed_operand_is_registered(); if (result) result = ensure_python_renameable_operand_is_registered(); if (result) result = ensure_python_target_operand_is_registered(); if (result) result = ensure_python_targetable_operand_is_registered(); + */ assert(result); diff --git a/plugins/pychrysalide/arch/operands/register.c b/plugins/pychrysalide/arch/operands/register.c index 2a48a0f..707524a 100644 --- a/plugins/pychrysalide/arch/operands/register.c +++ b/plugins/pychrysalide/arch/operands/register.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * register.c - équivalent Python du fichier "arch/operands/register.c" * - * Copyright (C) 2019-2020 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -30,25 +30,19 @@ #include <i18n.h> #include <arch/operands/register-int.h> -#include <plugins/dt.h> #include "../operand.h" #include "../register.h" #include "../../access.h" #include "../../helpers.h" -#include "../../glibext/bufferline.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_register_operand_new(PyTypeObject *, PyObject *, PyObject *); - -/* Initialise la classe des descriptions de fichier binaire. */ -static void py_register_operand_init_gclass(GRegisterOperandClass *, gpointer); +CREATE_DYN_CONSTRUCTOR(register_operand, G_TYPE_REGISTER_OPERAND); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_register_operand_init(PyObject *, PyObject *, PyObject *); @@ -58,12 +52,6 @@ static int py_register_operand_init(PyObject *, PyObject *, PyObject *); /* ------------------------- REGISTRE SOUS FORME D'OPERANDE ------------------------- */ -/* Compare un opérande avec un autre. */ -static PyObject *py_register_operand___cmp__(PyObject *, PyObject *); - -/* Traduit un opérande en version humainement lisible. */ -static PyObject *py_register_operand__print(PyObject *, PyObject *); - /* Fournit le registre associé à l'opérande. */ static PyObject *py_register_operand_get_register(PyObject *, void *); @@ -76,86 +64,6 @@ static PyObject *py_register_operand_get_register(PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_register_operand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_register_operand_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_REGISTER_OPERAND, type->tp_name, - (GClassInitFunc)py_register_operand_init_gclass, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * -* * -* Description : Initialise la classe des descriptions de fichier binaire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_register_operand_init_gclass(GRegisterOperandClass *class, gpointer unused) -{ - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -182,7 +90,7 @@ static int py_register_operand_init(PyObject *self, PyObject *args, PyObject *kw "\n" \ " RegisterOperand(reg)" \ "\n" \ - "Where reg is an architecture register defined from a subclass of" \ + "Where *reg* is an architecture register defined from a subclass of" \ " pychrysalide.arch.ArchRegister." /* Récupération des paramètres */ @@ -199,8 +107,8 @@ static int py_register_operand_init(PyObject *self, PyObject *args, PyObject *kw operand = G_REGISTER_OPERAND(pygobject_get(self)); - g_object_ref(G_OBJECT(reg)); - operand->reg = reg; + if (!g_register_operand_create(operand, reg)) + return -1; return 0; @@ -215,98 +123,6 @@ static int py_register_operand_init(PyObject *self, PyObject *args, PyObject *kw /****************************************************************************** * * -* Paramètres : self = serveur à manipuler. * -* args = arguments associés à l'appel. * -* * -* Description : Compare un opérande avec un autre. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_register_operand___cmp__(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GRegisterOperand *other; /* Autre opérande à manipuler */ - int ret; /* Bilan de lecture des args. */ - GRegisterOperand *operand; /* Elément à manipuler */ - int status; /* Bilan de comparaison */ - -#define REGISTER_OPERAND_CMP_METHOD PYTHON_METHOD_DEF \ -( \ - __cmp__, "$self, other, /", \ - METH_VARARGS, py_register_operand, \ - "Implementation of the required method used to compare the" \ - " operand with another one. This second object is always" \ - " a pychrysalide.arch.RegisterOperand instance.\n" \ - "\n" \ - "See the parent class for more information about this method." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_register_operand, &other); - if (!ret) return NULL; - - operand = G_REGISTER_OPERAND(pygobject_get(self)); - - status = g_arch_operand_compare(G_ARCH_OPERAND(operand), G_ARCH_OPERAND(other)); - - result = PyLong_FromLong(status); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = serveur à manipuler. * -* args = arguments associés à l'appel. * -* * -* Description : Traduit un opérande en version humainement lisible. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_register_operand__print(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GBufferLine *line; /* Ligne fournie à peupler */ - int ret; /* Bilan de lecture des args. */ - GRegisterOperand *operand; /* Elément à manipuler */ - -#define REGISTER_OPERAND_PRINT_METHOD PYTHON_METHOD_DEF \ -( \ - _print, "$self, line, /", \ - METH_VARARGS, py_register_operand, \ - "Implementation of the required method used to print the operand" \ - " into a rendering line, which is a provided" \ - " pychrysalide.glibext.BufferLine instance.\n" \ - "\n" \ - "See the parent class for more information about this method." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_buffer_line, &line); - if (!ret) return NULL; - - operand = G_REGISTER_OPERAND(pygobject_get(self)); - - g_arch_operand_print(G_ARCH_OPERAND(operand), line); - - result = Py_None; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * @@ -367,8 +183,6 @@ static PyObject *py_register_operand_get_register(PyObject *self, void *closure) PyTypeObject *get_python_register_operand_type(void) { static PyMethodDef py_register_operand_methods[] = { - REGISTER_OPERAND_CMP_METHOD, - REGISTER_OPERAND_PRINT_METHOD, { NULL } }; diff --git a/plugins/pychrysalide/arch/register.c b/plugins/pychrysalide/arch/register.c index 615a5b7..7139e47 100644 --- a/plugins/pychrysalide/arch/register.c +++ b/plugins/pychrysalide/arch/register.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * register.c - équivalent Python du fichier "arch/register.c" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -31,32 +31,27 @@ #include <i18n.h> #include <arch/register-int.h> -#include <plugins/dt.h> #include "../access.h" #include "../helpers.h" -#include "../analysis/storage/serialize.h" +#include "../glibext/comparable.h" +#include "../glibext/hashable.h" +#include "../glibext/serialize.h" +#include "../glibext/strbuilder.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_arch_register_new(PyTypeObject *, PyObject *, PyObject *); - /* Initialise la classe des registres. */ -static void py_arch_register_init_gclass(GArchRegisterClass *, gpointer); - -/* Produit une empreinte à partir d'un registre. */ -static guint py_arch_register___hash___wrapper(const GArchRegister *); +static int py_arch_register_init_gclass(GArchRegisterClass *, PyTypeObject *); -/* Compare un registre avec un autre. */ -static int py_arch_register___cmp___wrapper(const GArchRegister *, const GArchRegister *); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(arch_register, G_TYPE_ARCH_REGISTER); -/* Traduit un registre en version humainement lisible. */ -static void py_arch_register_print_wrapper(const GArchRegister *, GBufferLine *); +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_arch_register_init(PyObject *, PyObject *, PyObject *); /* Indique si le registre correspond à ebp ou similaire. */ static bool py_arch_register_is_base_pointer_wrapper(const GArchRegister *); @@ -69,9 +64,6 @@ static bool py_arch_register_is_stack_pointer_wrapper(const GArchRegister *); /* ---------------------------- PUR REGISTRE DU MATERIEL ---------------------------- */ -/* Effectue une comparaison avec un objet Python 'ArchRegister'. */ -static PyObject *py_arch_register_richcompare(PyObject *, PyObject *, int); - /* Indique si le registre correspond à ebp ou similaire. */ static PyObject *py_arch_register_is_base_pointer(PyObject *, void *); @@ -87,88 +79,8 @@ static PyObject *py_arch_register_is_stack_pointer(PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_arch_register_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de registre */ - bool status; /* Bilan d'un enregistrement */ - -#define ARCH_REGISTER_DOC \ - "The ArchRegister object aims to get subclassed to create" \ - " registers suitable for new architectures.\n" \ - "\n" \ - "Calls to the *__init__* constructor of this abstract object expect"\ - " no particular argument.\n" \ - "\n" \ - "The following methods have to be defined for new classes:\n" \ - "* pychrysalide.arch.ArchRegister.__hash__();\n" \ - "* pychrysalide.arch.ArchRegister.__cmp__();\n" \ - "* pychrysalide.arch.ArchRegister._print();\n" \ - "* pychrysalide.arch.ArchRegister._is_base_pointer();\n" \ - "* pychrysalide.arch.ArchRegister._is_stack_pointer().\n" \ - "\n" \ - "Chrysalide creates an internal glue to provide rich comparisons" \ - " for registers based on the old-style *__cmp__* function." - - /* Validations diverses */ - - base = get_python_arch_register_type(); - - if (type == base) - { - result = NULL; - PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); - goto exit; - } - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_ARCH_REGISTER, type->tp_name, - (GClassInitFunc)py_arch_register_init_gclass, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des registres. * * * @@ -178,190 +90,51 @@ static PyObject *py_arch_register_new(PyTypeObject *type, PyObject *args, PyObje * * ******************************************************************************/ -static void py_arch_register_init_gclass(GArchRegisterClass *class, gpointer unused) -{ - class->hash = py_arch_register___hash___wrapper; - class->compare = py_arch_register___cmp___wrapper; - class->print = py_arch_register_print_wrapper; - class->is_bp = py_arch_register_is_base_pointer_wrapper; - class->is_sp = py_arch_register_is_stack_pointer_wrapper; - -} - - -/****************************************************************************** -* * -* Paramètres : reg = registre visé par la procédure. * -* * -* Description : Produit une empreinte à partir d'un registre. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static guint py_arch_register___hash___wrapper(const GArchRegister *reg) -{ - guint result; /* Empreinte à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_REGISTER_HASH_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __hash__, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to produce a hash of the object. The" \ - " result must be an integer value." \ -) - - result = 0; - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(reg)); - - if (has_python_method(pyobj, "__hash__")) - { - pyret = run_python_method(pyobj, "__hash__", NULL); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLong(pyret); - - Py_DECREF(pyret); - - } - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier registre à consulter. * -* b = second registre à consulter. * -* * -* Description : Compare un registre avec un autre. * -* * -* Retour : Bilan de la comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static int py_arch_register___cmp___wrapper(const GArchRegister *a, const GArchRegister *b) +static int py_arch_register_init_gclass(GArchRegisterClass *gclass, PyTypeObject *pyclass) { - int result; /* Empreinte à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_REGISTER_CMP_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __cmp__, "$self, other, /", \ - METH_VARARGS, \ - "Abstract method used to compare the register with another" \ - " one. This second object is always an" \ - " pychrysalide.arch.ArchRegister instance.\n" \ - "\n" \ - " This is the Python old-style comparison method, but" \ - " Chrysalide provides a glue to automatically build a rich" \ - " version of this function." \ -) - - result = 0; - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(a)); - - if (has_python_method(pyobj, "__cmp__")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(b))); - - pyret = run_python_method(pyobj, "__cmp__", args); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsLong(pyret); - } + PY_CLASS_SET_WRAPPER(gclass->is_bp, py_arch_register_is_base_pointer_wrapper); + PY_CLASS_SET_WRAPPER(gclass->is_sp, py_arch_register_is_stack_pointer_wrapper); - Py_DECREF(args); - - Py_XDECREF(pyret); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return result; + return 0; } /****************************************************************************** * * -* Paramètres : reg = registre visé par la procédure. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Traduit un registre en version humainement lisible. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : - * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static void py_arch_register_print_wrapper(const GArchRegister *reg, GBufferLine *line) +static int py_arch_register_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_REGISTER_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _print, "$self, line, /", \ - METH_VARARGS, \ - "Abstract method used to print the register into a rendering" \ - " line, which is a provided pychrysalide.glibext.BufferLine" \ - " instance." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(reg)); - - if (has_python_method(pyobj, "_print")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); - - pyret = run_python_method(pyobj, "_print", args); - - Py_DECREF(args); + int ret; /* Bilan de lecture des args. */ - Py_XDECREF(pyret); +#define ARCH_REGISTER_DOC \ + "The ArchRegister object aims to get subclassed in order to create" \ + " registers suitable for new architectures.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect"\ + " no particular argument.\n" \ + "\n" \ + "The following methods may to be implemnted for new classes:\n" \ + "* pychrysalide.arch.ArchRegister._is_base_pointer();\n" \ + "* pychrysalide.arch.ArchRegister._is_stack_pointer().\n" - } + /* Initialisation d'un objet GLib */ - Py_DECREF(pyobj); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - PyGILState_Release(gstate); + return 0; } @@ -486,51 +259,6 @@ static bool py_arch_register_is_stack_pointer_wrapper(const GArchRegister *reg) /****************************************************************************** * * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * -* * -* Description : Effectue une comparaison avec un objet Python 'ArchRegister'.* -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_arch_register_richcompare(PyObject *a, PyObject *b, int op) -{ - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - const GArchRegister *reg_a; /* Premier élément à traiter */ - const GArchRegister *reg_b; /* Second élément à traiter */ - int status; /* Résultat d'une comparaison */ - - ret = PyObject_IsInstance(b, (PyObject *)get_python_arch_register_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - reg_a = G_ARCH_REGISTER(pygobject_get(a)); - reg_b = G_ARCH_REGISTER(pygobject_get(b)); - - status = py_arch_register___cmp___wrapper(reg_a, reg_b); - - result = status_to_rich_cmp_state(status, op); - - cmp_done: - - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * @@ -618,9 +346,6 @@ static PyObject *py_arch_register_is_stack_pointer(PyObject *self, void *closure PyTypeObject *get_python_arch_register_type(void) { static PyMethodDef py_arch_register_methods[] = { - ARCH_REGISTER_HASH_WRAPPER, - ARCH_REGISTER_CMP_WRAPPER, - ARCH_REGISTER_PRINT_WRAPPER, ARCH_REGISTER_IS_BASE_POINTER_WRAPPER, ARCH_REGISTER_IS_STACK_POINTER_WRAPPER, { NULL } @@ -643,11 +368,10 @@ PyTypeObject *get_python_arch_register_type(void) .tp_doc = ARCH_REGISTER_DOC, - .tp_richcompare = py_arch_register_richcompare, - .tp_methods = py_arch_register_methods, .tp_getset = py_arch_register_getseters, + .tp_init = py_arch_register_init, .tp_new = py_arch_register_new, }; @@ -683,9 +407,20 @@ bool ensure_python_arch_register_is_registered(void) dict = PyModule_GetDict(module); + if (!ensure_python_comparable_object_is_registered()) + return false; + + if (!ensure_python_hashable_object_is_registered()) + return false; + if (!ensure_python_serializable_object_is_registered()) return false; + if (!ensure_python_string_builder_is_registered()) + return false; + + pyg_register_class_init(G_TYPE_ARCH_REGISTER, (PyGClassInitFunc)py_arch_register_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_ARCH_REGISTER, type)) return false; diff --git a/plugins/pychrysalide/arch/vmpa.c b/plugins/pychrysalide/arch/vmpa.c index 20784cd..5162fac 100644 --- a/plugins/pychrysalide/arch/vmpa.c +++ b/plugins/pychrysalide/arch/vmpa.c @@ -824,13 +824,23 @@ PyObject *build_from_internal_vmpa(const vmpa2t *addr) PyTypeObject *type; /* Type à instancier */ PyObject *args; /* Liste des arguments d'appel */ - type = get_python_vmpa_type(); + if (get_phy_addr(addr) == VMPA_NO_PHYSICAL && get_virt_addr(addr) == VMPA_NO_VIRTUAL) + { + result = Py_None; + Py_INCREF(result); + } + + else + { + type = get_python_vmpa_type(); - args = Py_BuildValue("KK", get_phy_addr(addr), get_virt_addr(addr)); + args = Py_BuildValue("KK", get_phy_addr(addr), get_virt_addr(addr)); - result = PyObject_CallObject((PyObject *)type, args); + result = PyObject_CallObject((PyObject *)type, args); - Py_DECREF(args); + Py_DECREF(args); + + } return result; diff --git a/plugins/pychrysalide/bindings.c b/plugins/pychrysalide/bindings.c new file mode 100644 index 0000000..f120c3b --- /dev/null +++ b/plugins/pychrysalide/bindings.c @@ -0,0 +1,1523 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * bindings.c - éléments d'un socle commun aux fonctionnalités graphiques et non graphiques + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "bindings.h" + + +#include <assert.h> +#include <dlfcn.h> +#include <pygobject.h> +#include <stddef.h> +#include <stdio.h> + + +#include <common/cpp.h> +#include <common/environment.h> +#include <common/extstr.h> +#include <core/core.h> +#include <plugins/pglist.h> +#include <plugins/self.h> + + +#include "access.h" +#include "constants.h" +#include "helpers.h" +#include "star.h" +//#include "strenum.h" +#include "struct.h" +#include "analysis/module.h" +#include "arch/module.h" +#include "common/module.h" +#include "core/module.h" +#include "glibext/module.h" +/* #include "debug/module.h" */ +#include "format/module.h" +/* #include "mangling/module.h" */ +#include "plugins/module.h" + + + + + +/* ------------------------ FONCTIONNALITES POUR CODE PYTHON ------------------------ */ + + +#define PYCHRYSALIDE_NAME "pychrysalide" + +#define PYCHRYSALIDE_DOC \ + "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \ + "\n" \ + "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \ + "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \ + "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \ + "\n" \ + "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \ + "how the API works.\n" \ + "\n" \ + "These plugins are located in the 'plugins/python' directory.\n" \ + "\n" \ + "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup." + + +/* Fournit la révision du programme global. */ +static PyObject *py_chrysalide_revision(PyObject *, PyObject *); + +/* Fournit la version du programme global. */ +static PyObject *py_chrysalide_version(PyObject *, PyObject *); + +/* Fournit la version du greffon pour Python. */ +static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *); + + + +/* ------------------------ FONCTIONNALITES DE MISE EN PLACE ------------------------ */ + + +/* Détermine si l'interpréteur lancé est celui pris en compte. */ +static bool is_current_abi_suitable(void); + +/* Assure une pleine initialisation des objets de Python-GI. */ +static bool install_metaclass_for_python_gobjects(void); + +/* Met en place un environnement pour l'extension Python. */ +static bool setup_python_context(void); + +/* Intègre les éventuelles fonctions natives des interfaces. */ +static void inherit_interface_slots(PyObject *); + +/** + * Conservation d'anciens pointeurs remplacés + */ +static initproc __old_gobject_meta_base_init = NULL; +static initproc __old_gobject_meta_init = NULL; + +/** + * La fonction unhook_pygobject_behaviour(), inversant les opérations de la fonction + * unhook_pygobject_behaviour() manipulerait volontiers les fonctions PyImport_ImportModule() + * et PyObject_GetAttrString(). + * + * Cependant, les appels à ces dernières depuis la clôture organisée par la fonction + * PyExit_pychrysalide() provoque l'erreur suivante : + * + * Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) + * + * Les accès nécessaires sont donc conservés ici. + */ +static PyTypeObject *__gobject_meta_base = NULL; +static PyTypeObject *__gobject_meta = NULL; + +/* Interceptionne une initialisation pour types gi._gi.GObject. */ +static int hook_gobject_meta_base_init(PyObject *, PyObject *, PyObject *); + +/* Interceptionne une initialisation pour types GObject.Object. */ +static int hook_gobject_meta_init(PyObject *, PyObject *, PyObject *); + +/* Modifie légèrement le comportement des GObjects en Python. */ +static bool hook_pygobject_behaviour(void); + +/* Restaure le comportement d'origine des GObjects en Python. */ +static void unhook_pygobject_behaviour(void); + +/* Assure la définition d'un type GObject pour Python adapté. */ +static void ensure_native_pygobject_type(PyTypeObject **); + +/* Fournit la référence à un éventuel module déjà en place. */ +static PyObject *get_existing_modules(void); + +/* Définit les différents modules du support Python. */ +static PyObject *create_basic_modules(const pyinit_details_t *); + +/* Inscrit les défintions des objets Python de Chrysalide. */ +static bool populate_python_modules(const pyinit_details_t *); + +/* Restore une ancienne définition de type GObject au besoin. */ +static void restore_original_pygobject_type(PyTypeObject *); + + + +/* ------------------------ FONCTIONS GLOBALES DE CHRYSALIDE ------------------------ */ + + +/* Assure le plein chargement dans un interpréteur Python. */ +static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *); + +/* Point de sortie pour l'initialisation de Python. */ +static void PyExit_pychrysalide(void); + + + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES POUR CODE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la révision du programme global. * +* * +* Retour : Numéro de révision. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + +#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \ +( \ + revision, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the revision number of Chrysalide.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: 'r1665'." \ +) + + result = PyUnicode_FromString("r" XSTR(REVISION)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la version du programme global. * +* * +* Retour : Numéro de version. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_version(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + int major; /* Numéro de version majeur */ + int minor; /* Numéro de version mineur */ + int revision; /* Numéro de révision */ + char version[16]; /* Conservation temporaire */ + +#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \ +( \ + version, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the version number of Chrysalide.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: '1.6.65'." \ +) + + major = REVISION / 1000; + minor = (REVISION - (major * 1000)) / 100; + revision = REVISION % 100; + + snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision); + + result = PyUnicode_FromString(version); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la version du greffon pour Python. * +* * +* Retour : Numéro de version. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + char version[16]; /* Conservation temporaire */ + +#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \ +( \ + mod_version, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the version number of Chrysalide module for Python.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: '0.1.0'." \ +) + + snprintf(version, sizeof(version), "%s", "x.x.x");// FIXME _chrysalide_plugin.version); + + result = PyUnicode_FromString(version); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES DE MISE EN PLACE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Détermine si l'interpréteur lancé est celui pris en compte. * +* * +* Retour : true si l'exécution peut se poursuivre, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool is_current_abi_suitable(void) +{ + bool result; + int fds[2]; + int ret; + char cmds[128]; + char content[64]; + ssize_t got; + +#define GRAB_ABI_FLAGS_IN_PYTHON \ + "import sys" "\n" \ + "import os" "\n" \ + "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \ + "os.write(%d, data)" "\n" + + result = false; + + ret = pipe(fds); + if (ret == -1) + { + perror("pipe()"); + goto exit; + } + + snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]); + + ret = PyRun_SimpleString(cmds); + if (ret != 0) goto exit_with_pipe; + + got = read(fds[0], content, sizeof(content)); + if (got < 0) + { + perror("read()"); + goto exit_with_pipe; + } + + content[got] = '\0'; + + result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0); + + exit_with_pipe: + + close(fds[0]); + close(fds[1]); + + exit: + + if (!result) + PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \ + "the ones of the Python library used during the module compilation."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Assure une pleine initialisation des objets de Python-GI. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool install_metaclass_for_python_gobjects(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gi_types_mod; /* Module Python-GObject */ + + /** + * Les extensions Python sont chargées à partir de la fonction load_python_plugins(), + * qui fait appel à create_python_plugin(). Une instance y est construite via un + * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad + * dans le fichier __init__.py présent dans chaque module d'extension. + * + * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique + * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction + * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c. + * Le code de cette dernière comprend notamment la portion suivante : + * + * [...] + * Py_SET_TYPE(type, PyGObject_MetaType); + * [...] + * if (PyType_Ready(type) < 0) { + * g_warning ("couldn't make the type `%s' ready", type->tp_name); + * return; + * } + * [...] + * + * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c + * et commence par : + * + * int PyType_Ready(PyTypeObject *type) + * { + * if (type->tp_flags & Py_TPFLAGS_READY) { + * assert(_PyType_CheckConsistency(type)); + * return 0; + * } + * [...] + * } + * + * La vérification de cohérencce commence par analyser le type et son propre + * type : + * + * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c : + * + * int _PyType_CheckConsistency(PyTypeObject *type) + * { + * [...] + * CHECK(!_PyObject_IsFreed((PyObject *)type)); + * [...] + * } + * + * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c : + * + * int _PyObject_IsFreed(PyObject *op) + * { + * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) { + * return 1; + * } + * + * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL. + * + * Or le type du type est écrasé dans la fonction pygobject_register_class() + * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est + * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c : + * + * static PyObject * + * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass) + * { + * Py_INCREF(metaclass); + * PyGObject_MetaType = metaclass; + * Py_INCREF(metaclass); + * + * Py_SET_TYPE(&PyGObject_Type, metaclass); + * + * Py_INCREF(Py_None); + * return Py_None; + * } + * + * Afin de valider la vérification de _PyType_CheckConsistency() pour les + * modules externes qui entraînent un enregistrement tout en portant le drapeau + * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut + * initialiser au besoin la variable PyGObject_MetaType. + * + * Une ligne suffit donc à enregistrer le type intermédiaire : + * + * from _gi import types + * + * On simule ici une déclaration similaire si nécessaire, selon la valeur + * portée par PyGObject_Type.ob_base.ob_base.ob_type.tp_name : + * - "type" (PyType_Type) : état initial ; + * - "_GObjectMetaBase" : état revu. + */ + + /** + * PyGObject_Type.ob_base.ob_base.ob_type != &PyType_Type ? + */ + result = (PyType_CheckExact(&PyGObject_Type) == 0); + + if (!result) + { + gi_types_mod = PyImport_ImportModule("gi.types"); + + result = (PyErr_Occurred() == NULL); + + if (result) + result = (PyType_CheckExact(&PyGObject_Type) == 0); + + Py_XDECREF(gi_types_mod); + + } + +#ifndef NDEBUG + if (result) + assert(strcmp(PyGObject_Type.ob_base.ob_base.ob_type->tp_name, "_GObjectMetaBase") == 0); +#endif + + if (!result) + PyErr_SetString(PyExc_SystemError, "unable to install metaclass for Python GObjects."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Met en place un environnement pour l'extension Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool setup_python_context(void) +{ + bool result; /* Bilan à retourner */ + + result = false; + + /** + * Un message d'erreur doit être défini en cas d'échec de l'initialisation, + * via un appel à PyErr_SetString(). + */ + + if (!is_current_abi_suitable()) + goto exit; + + if (pygobject_init(-1, -1, -1) == NULL) + { + PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python."); + goto exit; + } + + if (!install_metaclass_for_python_gobjects()) + goto exit; + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : cls = classe instanciée pour la construction d'un objet. * +* * +* Description : Intègre les éventuelles fonctions natives des interfaces. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void inherit_interface_slots(PyObject *cls) +{ + GType gtype; /* Type GObject lié au Python */ + GType *ifaces; /* Interfaces implémentées */ + guint ifaces_count; /* Nombre de ces interfaces */ + guint i; /* Boucle de parcours */ + PyTypeObject *iface_type; /* Type Python pour interface */ + size_t k; /* Boucle de parcours */ + size_t offset; /* Position dans une structure */ + void *src_slot; /* Eventuelle fonction idéale */ + void *dst_slot; /* Eventuelle fonction en place*/ + + static size_t slot_offsets[] = { /* Emplacements à actualiser */ + //offsetof(PyTypeObject, tp_str), + offsetof(PyTypeObject, tp_hash), + offsetof(PyTypeObject, tp_richcompare), + }; + + /** + * Cette fonction reprend les principes de la fonction d'importation de + * PyGObject pygobject_inherit_slots(). + * + * Cependant, cette dernière n'est appelée que depuis les fonctions : + * - pygobject_register_class() (send C -> Python), qui peut écraser des + * slots existants ; + * - pygobject_new_with_interfaces() / pygobject_lookup_class(), qui ne + * remplace pas les fonctions par défaut déjà en place. + * + * Pour mémoire, les types créés dynamiquement depuis des scripts (sens + * Python -> C) passent par la fonction _wrap_pyg_type_register(). + */ + + gtype = pyg_type_from_object(cls); + assert(gtype != G_TYPE_INVALID); + + ifaces = g_type_interfaces(gtype, &ifaces_count); + + for (i = 0; i < ifaces_count; i++) + { + iface_type = pygobject_lookup_class(ifaces[i]); + +#define PYTYPE_SLOT(tp, off) \ + *(void **)(void *)(((char *)tp) + off) + + for (k = 0; k < ARRAY_SIZE(slot_offsets); k++) + { + offset = slot_offsets[k]; + + src_slot = PYTYPE_SLOT(iface_type, offset); + + if (src_slot == NULL) + continue; + + if (src_slot == PYTYPE_SLOT(&PyBaseObject_Type, offset) + || src_slot == PYTYPE_SLOT(&PyGObject_Type, offset)) + continue; + + dst_slot = PYTYPE_SLOT(cls, offset); + + if (src_slot == dst_slot) + continue; + + if (dst_slot != NULL) + { + if (dst_slot != PYTYPE_SLOT(&PyBaseObject_Type, offset) + && dst_slot != PYTYPE_SLOT(&PyGObject_Type, offset)) + continue; + } + + /** + * Usage du *(void **)(void *) + */ + PYTYPE_SLOT(cls, offset) = src_slot; + + } + + } + + g_free(ifaces); + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Interceptionne une initialisation pour types gi._gi.GObject. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int hook_gobject_meta_base_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + + /** + * Le type de self (self->ob_type->tp_name) est ici _GObjectMetaBase. + */ + + result = __old_gobject_meta_base_init(self, args, kwds); + + if (result == 0) + inherit_interface_slots(self); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Interceptionne une initialisation pour types GObject.Object. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int hook_gobject_meta_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + + /** + * Le type de self (self->ob_type->tp_name) est ici GObjectMeta. + */ + + result = __old_gobject_meta_init(self, args, kwds); + + if (result == 0) + inherit_interface_slots(self); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Modifie légèrement le comportement des GObjects en Python. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool hook_pygobject_behaviour(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gi_types_mod; /* Module Python-GObject */ + + result = false; + + /** + * Validation des accès. + * + * Les références prises sur les attributs sont restituées dans + * unhook_pygobject_behaviour(). + */ + + gi_types_mod = PyImport_ImportModule("gi.types"); + if (gi_types_mod == NULL) goto exit; + + __gobject_meta_base = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "_GObjectMetaBase"); + assert(__gobject_meta_base != NULL); + if (__gobject_meta_base == NULL) goto exit_with_mod; + + __gobject_meta = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "GObjectMeta"); + assert(__gobject_meta != NULL); + if (__gobject_meta == NULL) goto exit_with_mod; + + /** + * Modification des comportements. + */ + + __old_gobject_meta_base_init = __gobject_meta_base->tp_init; + + __gobject_meta_base->tp_init = hook_gobject_meta_base_init; + + __old_gobject_meta_init = __gobject_meta->tp_init; + + __gobject_meta->tp_init = hook_gobject_meta_init; + + result = true; + + exit_with_mod: + + Py_DECREF(gi_types_mod); + + exit: + + if (!result) + PyErr_SetString(PyExc_SystemError, "unable to hook the GObject behaviour in Python."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Restaure le comportement d'origine des GObjects en Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void unhook_pygobject_behaviour(void) +{ + /** + * Le déclenchement de la fonction PyExit_pychrysalide() appelante est + * programmé depuis init_python_pychrysalide_module(), appelée si + * hook_pygobject_behaviour() a opéré avec réussite. + */ + assert(__gobject_meta_base != NULL); + assert(__gobject_meta != NULL); + + __gobject_meta_base->tp_init = __old_gobject_meta_base_init; + + Py_XDECREF(__gobject_meta_base); + + __gobject_meta->tp_init = __old_gobject_meta_init; + + Py_XDECREF(__gobject_meta); + +} + + +/****************************************************************************** +* * +* Paramètres : namespace = module particulier à charger à partir de gi. * +* version = idenfiant de la version à stipuler. * +* * +* Description : Charge un module GI dans Python avec une version attendue. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool import_namespace_from_gi_repository(const char *namespace, const char *version) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Module Python-GObject */ + PyObject *args; /* Arguments à fournir */ + int ret; /* Bilan d'une mise en place */ + + result = false; + + /* Sélection d'une version */ + + module = PyImport_ImportModule("gi"); + + if (module != NULL) + { + args = Py_BuildValue("ss", namespace, version); + + run_python_method(module, "require_version", args); + + result = (PyErr_Occurred() == NULL); + + Py_DECREF(args); + Py_DECREF(module); + + } + + /* Importation du module visé */ + + if (result) + { + args = PyTuple_New(1); + + ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace)); + if (ret != 0) + { + result = false; + goto args_error; + } + + module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args); + + result = (module != NULL); + + Py_XDECREF(module); + + args_error: + + Py_DECREF(args); + + } + + // TODO : message d'erreur ? + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : py_gobj_def = définition de type actuelle. [OUT] * +* * +* Description : Assure la définition d'un type GObject pour Python adapté. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void ensure_native_pygobject_type(PyTypeObject **py_gobj_def) +{ + GQuark pygobject_class_key; /* Copie d'un accès GI interne */ + + /** + * Les appels suivants procèdent à l'enregistrement de différents éléments + * dans l'espace de noms Python pour Chrysalide. + * + * Une majeure partie de ces éléments est constituée d'objets dérivés de + * GObject. Ce type d'objet (G_TYPE_OBJECT) est représenté par deux types + * en Python : + * + * - gi._gi.GObject, mis en place lors de l'importation du module gi. + * + * Ce dernier lance automatiquement l'importation du module natif gi._gi, + * lequel, via son initialisation dans la fonction PyInit__gi() + * (cf. ./gi/gimodule.c) lance un appel à pyi_object_register_types() + * qui procède à l'enregistrement du type "GObject" porté par la structure + * PyGObject_Type en correspondance au type GLib G_TYPE_OBJECT (cf. appel + * à pygobject_register_class() dans gi/pygobject-object.c). + * + * - gi.repository.GObject.Object, qui vient surclasser le type précédent + * lors d'un appel d'initialisation : from gi.repository import GObject. + * + * Cette seconde définition est destinée à apporter une représentation + * de l'introspection GObject de plus haut niveau pour l'utilisateur par + * rapport à celle de bas niveau gi._gi. + * + * Il demeure que la seconde définition est entièrement implémentée en Python + * et porte ainsi le fanion Py_TPFLAGS_HEAPTYPE, imposant cette même dernière + * propriétée à tous les objets qui en dérivent. + * + * Les définitions de Chrysalide sont cependant toutes statiques et donc + * incompatibles avec une définition gi.repository.GObject.Object, comme le + * pointent les validations opérées par PyType_Ready(). + * + * Une solution consiste ici à restaurer au besoin la définition gi._gi.GObject + * brute, effectuer les enregistrements de Chrysalide sur cette base, et + * remettre en place la définition éventuellement remplacée ensuite. + */ + + *py_gobj_def = pygobject_lookup_class(G_TYPE_OBJECT); + + if (*py_gobj_def != &PyGObject_Type) + { + Py_INCREF((PyObject *)*py_gobj_def); + + /* Définition récupérée de pyi_object_register_types() */ + pygobject_class_key = g_quark_from_static_string("PyGObject::class"); + + g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, &PyGObject_Type); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit la référence à un éventuel module déjà en place. * +* * +* Retour : Pointeur vers le module mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *get_existing_modules(void) +{ + PyObject *result; /* Module Python à retourner */ + + /** + * Vérification préalable : dans le cas où on est embarqué directement dans + * un interpréteur Python, le module se charge et termine par charger à leur + * tour les différentes extensions trouvées, via load_remaning_plugins() puis + * chrysalide_plugin_on_native_loaded(). + * + * Lesquelles vont très probablement charger le module pychrysalide. + * + * Comme le chargement de ce dernier n'est alors pas encore terminé, + * Python va relancer cette procédure, et register_access_to_python_module() + * va détecter un doublon. + */ + + result = get_access_to_python_module(PYCHRYSALIDE_NAME); + + Py_XINCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Définit les différents modules du support Python. * +* * +* Retour : Pointeur vers le module mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *create_basic_modules(const pyinit_details_t *details) +{ + PyObject *result; /* Module Python à retourner */ + bool status; /* Bilan des inclusions */ + + static PyMethodDef py_chrysalide_methods[] = { + PY_CHRYSALIDE_REVISION_METHOD, + PY_CHRYSALIDE_VERSION_METHOD, + PY_CHRYSALIDE_MOD_VERSION_METHOD, + { NULL } + }; + + static PyModuleDef py_chrysalide_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = PYCHRYSALIDE_NAME, + .m_doc = PYCHRYSALIDE_DOC, + + .m_size = -1, + + .m_methods = py_chrysalide_methods + + }; + + result = PyModule_Create(&py_chrysalide_module); + + register_access_to_python_module(py_chrysalide_module.m_name, result); + + status = true; + + /** + * Réceptacle pour objets et constantes : à laisser en premier ajout donc. + */ + if (status) status = add_features_module(result); + + if (status) status = define_data_types_constants(result); + + if (status) status = add_analysis_module(result); + if (status) status = add_arch_module(result); + if (status) status = add_common_module(result); + if (status) status = add_glibext_module(result); + if (status) status = add_core_module(result); + /* + if (status) status = add_debug_module(result); + */ + if (status) status = add_format_module(result); + /* + if (status) status = add_mangling_module(result); + */ + if (status) status = add_plugins_module(result); + + /** + * Ajout de modules UI supplémentaires éventuels. + */ + + if (status && details->add_extra != NULL) + status = details->add_extra(result); + + if (!status) + { + Py_DECREF(result); + result = NULL; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Inscrit les défintions des objets Python de Chrysalide. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool populate_python_modules(const pyinit_details_t *details) +{ + bool result; /* Bilan à retourner */ + + /** + * Les chargements de types supplémentaires, apportés par la version UI, ne + * peuvent être forcés depuis les mises en places des versions non-UI via + * les classiques appels ensure_xxx(). + * + * L'assurance d'un chargement préalable est ainsi réalisée ici, via + * un chargement préliminaire, si besoin est. + */ + + if (details->populate_extra != NULL) + result = details->populate_extra(false); + else + result = true; + + /* + if (result) result = ensure_python_string_enum_is_registered(); + */ + if (result) result = ensure_python_py_struct_is_registered(); + + if (result) result = populate_analysis_module(); + if (result) result = populate_arch_module(); + if (result) result = populate_glibext_module(); + if (result) result = populate_common_module(); + if (result) result = populate_core_module(); + /* + if (result) result = populate_debug_module(); + */ + if (result) result = populate_format_module(); + /* + if (result) result = populate_mangling_module(); + */ + if (result) result = populate_plugins_module(); + + /** + * Certaines définitions reposent sur une déclinaison de GtkWidget, + * dont le chargement va remplacer la définition statique de GObject + * par une version allouée dynamiquement. + * + * De telles définitions doivent donc être prise en compte à la fin + * du chargement. + */ + + if (result && details->populate_extra != NULL) + result = details->populate_extra(true); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : py_gobj_def = définition de type actuelle. [OUT] * +* * +* Description : Restore une ancienne définition de type GObject au besoin. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void restore_original_pygobject_type(PyTypeObject *py_gobj_def) +{ + GQuark pygobject_class_key; /* Copie d'un accès GI interne */ + + if (py_gobj_def != &PyGObject_Type) + { + /* Définition récupérée de pyi_object_register_types() */ + pygobject_class_key = g_quark_from_static_string("PyGObject::class"); + + g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, py_gobj_def); + + Py_DECREF((PyObject *)py_gobj_def); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Implémente le point d'entrée pour l'initialisation de Python.* +* * +* Retour : Module mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *init_python_pychrysalide_module(const pyinit_details_t *details) +{ + PyObject *result; /* Module Python à retourner */ + PyTypeObject *py_gobj_def; /* Définition GObject courante */ + bool status; /* Bilan des inclusions */ + + result = get_existing_modules(); + + if (result != NULL) + return result; + + if (!setup_python_context()) + goto exit; + + if (!hook_pygobject_behaviour()) + goto exit; + + /** + * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil, + * à savoir des types convertis de façon incomplète. Par exemple, pour une + * structure GChecksum, le type à l'exécution est : + * + * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>] + * + * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>] + * + * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec + * la version debug de Python3 (version de python3-gi : 3.42.2-3) : + * + * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module() + * + * Code de reproduction dans un interpréteur classique : + * + * import gi + * gi.require_version('GLib', '2.0') + * from gi.repository import GLib + * + */ + + if (!import_namespace_from_gi_repository("GLib", "2.0")) + goto exit; + + /* Mise en place des fonctionnalités offertes */ + + ensure_native_pygobject_type(&py_gobj_def); + + result = create_basic_modules(details); + + if (result == NULL) + PyErr_SetString(PyExc_SystemError, "failed to create all PyChrysalide modules."); + + else + { + status = populate_python_modules(details); + + if (!status) + PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components."); + + else if (details->standalone) + status = init_python_interpreter_for_standalone_mode(details); + + if (!status) + { + Py_DECREF(result); + result = NULL; + } + + } + + restore_original_pygobject_type(py_gobj_def); + + exit: + + if (result == NULL && !details->standalone) + log_pychrysalide_exception("Python bindings loading failed"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.* +* * +* Description : Présente dans le journal une exception survenue. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void log_pychrysalide_exception(const char *prefix, ...) +{ + va_list ap; /* Compléments argumentaires */ + char *msg; /* Message complet à imprimer */ + PyObject *err_type; /* Type d'erreur Python */ + PyObject *err_value; /* Instance Python d'erreur */ + PyObject *err_traceback; /* Trace Python associée */ + PyObject *err_string; /* Description Python d'erreur */ + const char *err_msg; /* Représentation humaine */ + + assert(PyGILState_Check() == 1); + + if (PyErr_Occurred()) + { + /* Base de la communication */ + + va_start(ap, prefix); + + vasprintf(&msg, prefix, ap); + + va_end(ap); + + /* Détails complémentaires */ + + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + PyErr_NormalizeException(&err_type, &err_value, &err_traceback); + + if (err_traceback == NULL) + { + err_traceback = Py_None; + Py_INCREF(err_traceback); + } + + PyException_SetTraceback(err_value, err_traceback); + + if (err_value == NULL) + msg = stradd(msg, _(": no extra information is provided...")); + + else + { + err_string = PyObject_Str(err_value); + err_msg = PyUnicode_AsUTF8(err_string); + + msg = stradd(msg, ": "); + msg = stradd(msg, err_msg); + + Py_DECREF(err_string); + + } + + /** + * Bien que la documentation précise que la fonction PyErr_Fetch() + * transfère la propritété des éléments retournés, la pratique + * montre que le programme plante à la terminaison en cas d'exception. + * + * C'est par exemple le cas quand un greffon Python ne peut se lancer + * correctement ; l'exception est alors levée à partir de la fonction + * create_python_plugin() et le plantage intervient en sortie d'exécution, + * au moment de la libération de l'extension Python : + * + * ==14939== Jump to the invalid address stated on the next line + * ==14939== at 0x1A8FCBC9: ??? + * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3) + * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370) + * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153) + * ==14939== by 0x10AD19: main (main.c:440) + * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd + * + * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un + * appel à PyErr_PrintEx(0) ne change rien. + * + * La seule différence de l'instruction set_sys_last_vars réside en quelques + * lignes dans le code de l'interpréteur Python : + * + * if (set_sys_last_vars) { + * _PySys_SetObjectId(&PyId_last_type, exception); + * _PySys_SetObjectId(&PyId_last_value, v); + * _PySys_SetObjectId(&PyId_last_traceback, tb); + * } + * + * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ? + * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence + * à ces éléments. + * + * On reproduit ici le comportement du code correcteur avec PySys_SetObject(). + */ + + PySys_SetObject("last_type", err_type); + PySys_SetObject("last_value", err_value); + PySys_SetObject("last_traceback", err_traceback); + + Py_XDECREF(err_traceback); + Py_XDECREF(err_value); + Py_XDECREF(err_type); + + log_plugin_simple_message(LMT_EXT_ERROR, msg); + + free(msg); + + } + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = instance représentant le greffon courant. * +* path = chemin supplémentaire pour l'espace de recherche. * +* * +* Description : Complète les chemins de recherches de Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void extend_python_path(const GPluginModule *plugin, const char *path) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *list; /* Liste de chemins à compléter*/ + PyObject *new; /* Nouveau chemin à intégrer */ + + gstate = PyGILState_Ensure(); + + list = PySys_GetObject("path"); + assert(list != NULL); + + new = PyUnicode_FromString(path); + assert(new != NULL); + + PyList_Append(list, new); + + Py_DECREF(new); + + add_to_env_var("PYTHONPATH", path, ":"); + + PyGILState_Release(gstate); + + g_plugin_module_log_variadic_message(plugin, LMT_INFO, + _("PYTHONPATH environment variable set to '%s'"), + getenv("PYTHONPATH")); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONS GLOBALES DE CHRYSALIDE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Assure le plein chargement dans un interpréteur Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *details) +{ + bool result; /* Bilan à retourner */ + int ret; /* Bilan de préparatifs */ + Dl_info info; /* Informations dynamiques */ + GModule *module; /* Structure de chargement GLib*/ + GPluginModule *self; /* Représentation interne */ + + result = false; + + ret = Py_AtExit(PyExit_pychrysalide); + if (ret == -1) + { + PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function."); + goto exit; + } + + if (!load_core_components(ACC_ALL_COMPONENTS)) + { + PyErr_SetString(PyExc_SystemError, "unable to load core components."); + goto exit; + } + + /** + * Le module chargé par Python n'apparaît pas dans la liste des greffons de + * Chrysalide et ne peut donc pas être référencé comme dépendance par d'autres + * extensions. + * + * Par ailleurs, lors de la recherche d'autres greffons via l'appel à la + * fonction init_all_plugins() ci-après, il faut que le nom du greffon soit + * déjà réservé pour faire échouer le second chargement du greffon courant + * lors du parcours des répertoires conservant les fichiers d'extensions. + */ + + ret = dladdr(__FUNCTION__, &info); + if (ret == 0) + { + LOG_ERROR_DL_N("dladdr"); + + PyErr_SetString(PyExc_SystemError, "failed to force bindings registration."); + goto exit; + + } + + module = g_module_open(info.dli_fname, G_MODULE_BIND_LAZY); + assert(module != NULL); + + self = details->create_self(module); + + /* A ce stade, le greffon a été chargé correctement */ + g_plugin_module_override_flags(self, PSF_LOADED); + + register_plugin(self); + + unref_object(self); + + /** + * Intégration des fonctionnalités portées par d'autres greffons. + */ + + result = true; + + init_all_plugins(true); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Point de sortie pour l'initialisation de Python. * +* * +* Retour : ? * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void PyExit_pychrysalide(void) +{ + unhook_pygobject_behaviour(); + + exit_all_plugins(); + + unload_core_components(ACC_ALL_COMPONENTS); + +} diff --git a/plugins/pychrysalide/bindings.h b/plugins/pychrysalide/bindings.h new file mode 100644 index 0000000..036f852 --- /dev/null +++ b/plugins/pychrysalide/bindings.h @@ -0,0 +1,86 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * bindings.h - prototypes pour les éléments d'un socle commun aux fonctionnalités graphiques et non graphiques + * + * Copyright (C) 2024-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_BINDINGS_H +#define _PLUGINS_PYCHRYSALIDE_BINDINGS_H + + +/** + * Note: + * Since Python may define some pre-processor definitions which affect the standard headers + * on some systems, you must include Python.h before any standard headers are included. + * + * cf. https://docs.python.org/3.4/c-api/intro.html + */ +#include <Python.h> + + +#include <gmodule.h> +#include <stdbool.h> + + +#include <plugins/plugin.h> + + + +/* ------------------------ FONCTIONNALITES DE MISE EN PLACE ------------------------ */ + + +/* Charge un module GI dans Python avec une version attendue. */ +bool import_namespace_from_gi_repository(const char *, const char *); + +/* Raffinements pour la mise en place du module Python */ +typedef struct _pyinit_details_t +{ + bool standalone; /* Chargement depuis Python ? */ + + bool (* add_extra) (PyObject *); /* Ajout de modules ? */ + + bool (* populate_extra) (bool); /* Ajout de types ? */ + + /** + * Prototype de la fonction de création, à garder synchronisé avec + * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h). + */ + GPluginModule * (* create_self) (GModule *); + +} pyinit_details_t; + +/* Implémente le point d'entrée pour l'initialisation de Python. */ +PyObject *init_python_pychrysalide_module(const pyinit_details_t *); + +/* Présente dans le journal une exception survenue. */ +void log_pychrysalide_exception(const char *, ...); + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Complète les chemins de recherches de Python. */ +void extend_python_path(const GPluginModule *, const char *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_BINDINGS_H */ diff --git a/plugins/pychrysalide/common/Makefile.am b/plugins/pychrysalide/common/Makefile.am index b5249b9..ad58900 100644 --- a/plugins/pychrysalide/common/Makefile.am +++ b/plugins/pychrysalide/common/Makefile.am @@ -1,16 +1,22 @@ noinst_LTLIBRARIES = libpychrysacommon.la +# libpychrysacommon_la_SOURCES = \ +# bits.h bits.c \ +# fnv1a.h fnv1a.c \ +# hex.h hex.c \ +# itoa.h itoa.c \ +# module.h module.c \ +# packed.h packed.c \ +# pathname.h pathname.c \ +# pearson.h pearson.c + libpychrysacommon_la_SOURCES = \ bits.h bits.c \ - fnv1a.h fnv1a.c \ - hex.h hex.c \ - itoa.h itoa.c \ + entropy.h entropy.c \ leb128.h leb128.c \ module.h module.c \ - packed.h packed.c \ - pathname.h pathname.c \ - pearson.h pearson.c + xdg.h xdg.c libpychrysacommon_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/common/bits.c b/plugins/pychrysalide/common/bits.c index a127251..35d6ea4 100644 --- a/plugins/pychrysalide/common/bits.c +++ b/plugins/pychrysalide/common/bits.c @@ -534,27 +534,33 @@ static PyObject *py_bitfield_set_all(PyObject *self, PyObject *args) static PyObject *py_bitfield_reset(PyObject *self, PyObject *args) { - unsigned long first; /* Indice du premier bit testé */ unsigned long count; /* Nombre de bits à analyser */ + unsigned long first; /* Indice du premier bit testé */ int ret; /* Bilan de lecture des args. */ py_bitfield_t *bf; /* Instance à manipuler */ #define BITFIELD_RESET_METHOD PYTHON_METHOD_DEF \ ( \ - reset, "$self, first, count, /", \ + reset, "$self, first, /, count=1", \ METH_VARARGS, py_bitfield, \ "Switch to 0 a part of bits in a bitfield.\n" \ "\n" \ "The area to process starts at bit *first* and has a" \ - " size of *count* bits." \ + " size of *count* bits (only one bit is processed if" \ + " no *size* is provided)." \ ) - ret = PyArg_ParseTuple(args, "kk", &first, &count); + count = 1; + + ret = PyArg_ParseTuple(args, "k|k", &first, &count); if (!ret) return NULL; bf = (py_bitfield_t *)self; - reset_in_bit_field(bf->native, first, count); + if (count == 1) + reset_in_bit_field(bf->native, first); + else + reset_multi_in_bit_field(bf->native, first, count); Py_RETURN_NONE; @@ -576,27 +582,33 @@ static PyObject *py_bitfield_reset(PyObject *self, PyObject *args) static PyObject *py_bitfield_set(PyObject *self, PyObject *args) { - unsigned long first; /* Indice du premier bit testé */ unsigned long count; /* Nombre de bits à analyser */ + unsigned long first; /* Indice du premier bit testé */ int ret; /* Bilan de lecture des args. */ py_bitfield_t *bf; /* Instance à manipuler */ #define BITFIELD_SET_METHOD PYTHON_METHOD_DEF \ ( \ - set, "$self, first, count, /", \ + set, "$self, first, /, count=1", \ METH_VARARGS, py_bitfield, \ "Switch to 1 a part of bits in a bitfield.\n" \ "\n" \ "The area to process starts at bit *first* and has a" \ - " size of *count* bits." \ + " size of *count* bits (only one bit is processed if" \ + " no *size* is provided)." \ ) - ret = PyArg_ParseTuple(args, "kk", &first, &count); + count = 1; + + ret = PyArg_ParseTuple(args, "k|k", &first, &count); if (!ret) return NULL; bf = (py_bitfield_t *)self; - set_in_bit_field(bf->native, first, count); + if (count == 1) + set_in_bit_field(bf->native, first); + else + set_multi_in_bit_field(bf->native, first, count); Py_RETURN_NONE; diff --git a/plugins/pychrysalide/common/entropy.c b/plugins/pychrysalide/common/entropy.c new file mode 100644 index 0000000..2817747 --- /dev/null +++ b/plugins/pychrysalide/common/entropy.c @@ -0,0 +1,119 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * entropy.c - équivalent Python du fichier "common/entropy.c" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "entropy.h" + + +#include <common/entropy.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* Détermine l'entropie d'un contenu binaire. */ +static PyObject *py_entropy_compute_entropy(PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = arguments fournis lors de l'appel à la fonction. * +* * +* Description : Détermine l'entropie d'un contenu binaire. * +* * +* Retour : Valeur d'entropie du contenu fourni. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_entropy_compute_entropy(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + const bin_t *data; /* Données à traiter */ + int bits; /* Choix de référentiel */ + size_t len; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + double entropy; /* Valeur d'entropie déterminée*/ + +#define COMPUTE_ENTROPY_METHOD PYTHON_METHOD_DEF \ +( \ + compute_entropy, "data, /, bits=False", \ + METH_VARARGS, py_entropy, \ + "Compute the entropy of provided data.\n" \ + "\n" \ + "The *data* to process can be a string or bytes." \ + " The optional *bits* argument defines if the" \ + " computed value is expressed in bits (log base 2)" \ + " or in bytes (log base 256).\n" \ + "\n" \ + "The result is a float value." \ +) + + bits = 0; + + ret = PyArg_ParseTuple(args, "s#|p", &data, &len, &bits); + if (!ret) return NULL; + + entropy = compute_entropy((const bin_t *)data, len, bits); + + result = PyFloat_FromDouble(entropy); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Définit une extension du module 'common' à compléter. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_common_module_with_entropy(void) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Module à recompléter */ + + static PyMethodDef py_entropy_methods[] = { + COMPUTE_ENTROPY_METHOD, + { NULL } + }; + + module = get_access_to_python_module("pychrysalide.common"); + + result = register_python_module_methods(module, py_entropy_methods); + + return result; + +} diff --git a/plugins/pychrysalide/plugins/translate.h b/plugins/pychrysalide/common/entropy.h index facffd2..3fe5e28 100644 --- a/plugins/pychrysalide/plugins/translate.h +++ b/plugins/pychrysalide/common/entropy.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * translate.h - prototypes pour la conversion de structures liées aux greffons en objets Python + * entropy.h - prototypes pour l'équivalent Python du fichier "common/entropy.c" * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,20 +22,18 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H -#define _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H +#ifndef _PLUGINS_PYCHRYSALIDE_COMMON_ENTROPY_H +#define _PLUGINS_PYCHRYSALIDE_COMMON_ENTROPY_H #include <Python.h> +#include <stdbool.h> -#include <plugins/plugin-def.h> +/* Définit une extension du module 'common' à compléter. */ +bool populate_common_module_with_entropy(void); -/* Traduit une description d'interface de greffon. */ -PyObject *translate_plugin_interface_to_python(const plugin_interface *); - - -#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_COMMON_ENTROPY_H */ diff --git a/plugins/pychrysalide/common/leb128.c b/plugins/pychrysalide/common/leb128.c index 8b15303..2eeb191 100644 --- a/plugins/pychrysalide/common/leb128.c +++ b/plugins/pychrysalide/common/leb128.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * leb128.c - équivalent Python du fichier "common/leb128.c" * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,13 +26,13 @@ #include <assert.h> +#include <malloc.h> #include <pygobject.h> #include <common/leb128.h> -#include "packed.h" #include "../access.h" #include "../helpers.h" @@ -69,31 +69,29 @@ static PyObject *py_leb128_pack_uleb128(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ uleb128_t value; /* Valeur à manipuler */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ int ret; /* Bilan de lecture des args. */ - bool status; /* Bilan de l'opération */ - -#define LEB128_PACK_ULEB128_METHOD PYTHON_METHOD_DEF \ -( \ - pack_uleb128, "value, pbuf", \ - METH_VARARGS, py_leb128, \ - "Pack an unsigned LEB128 value into a data buffer.\n" \ - "\n" \ - "The *value* is an integer value. The *pbuf* argument has to" \ - " be a pychrysalide.common.PackedBuffer instance where data" \ - " will be appended.\n" \ - "\n" \ - "The returned value is the operation status: *True* for" \ - " success, *False* for failure." \ + size_t count; /* Nombre d'octets produits */ + void *bytes; /* Octets de représentation */ + +#define LEB128_PACK_ULEB128_METHOD PYTHON_METHOD_DEF \ +( \ + pack_uleb128, "value", \ + METH_VARARGS, py_leb128, \ + "Pack an unsigned LEB128 value into bytes.\n" \ + "\n" \ + "The *value* has to be an integer value.\n" \ + "\n" \ + "The returned value is byte data." \ ) - ret = PyArg_ParseTuple(args, "O&O&", convert_to_uleb128_value, &value, convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "O&", convert_to_uleb128_value, &value); if (!ret) return NULL; - status = pack_uleb128(&value, pbuf); + bytes = pack_uleb128(&value, &count); - result = status ? Py_True : Py_False; - Py_INCREF(result); + result = PyBytes_FromStringAndSize(bytes, count); + + free(bytes); return result; @@ -117,31 +115,29 @@ static PyObject *py_leb128_pack_leb128(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ leb128_t value; /* Valeur à manipuler */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ int ret; /* Bilan de lecture des args. */ - bool status; /* Bilan de l'opération */ - -#define LEB128_PACK_LEB128_METHOD PYTHON_METHOD_DEF \ -( \ - pack_leb128, "value, pbuf", \ - METH_VARARGS, py_leb128, \ - "Pack a signed LEB128 value into a data buffer.\n" \ - "\n" \ - "The *value* is an integer value. The *pbuf* argument has to" \ - " be a pychrysalide.common.PackedBuffer instance where data" \ - " will be appended.\n" \ - "\n" \ - "The returned value is the operation status: *True* for" \ - " success, *False* for failure." \ + size_t count; /* Nombre d'octets produits */ + void *bytes; /* Octets de représentation */ + +#define LEB128_PACK_LEB128_METHOD PYTHON_METHOD_DEF \ +( \ + pack_leb128, "value", \ + METH_VARARGS, py_leb128, \ + "Pack a signed LEB128 value into bytes.\n" \ + "\n" \ + "The *value* has to be an integer value.\n" \ + "\n" \ + "The returned value is byte data." \ ) - ret = PyArg_ParseTuple(args, "O&O&", convert_to_leb128_value, &value, convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "O&", convert_to_leb128_value, &value); if (!ret) return NULL; - status = pack_leb128(&value, pbuf); + bytes = pack_leb128(&value, &count); + + result = PyBytes_FromStringAndSize(bytes, count); - result = status ? Py_True : Py_False; - Py_INCREF(result); + free(bytes); return result; @@ -164,33 +160,42 @@ static PyObject *py_leb128_pack_leb128(PyObject *self, PyObject *args) static PyObject *py_leb128_unpack_uleb128(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + const char *bytes; /* Octets brutes transmis */ + Py_ssize_t count; /* Quantité de ces octets */ int ret; /* Bilan de lecture des args. */ + const void *pos; /* Tëte de lecture */ + const void *max; /* Position de lecture maximale*/ uleb128_t value; /* Valeur à manipuler */ bool status; /* Bilan de l'opération */ #define LEB128_UNPACK_ULEB128_METHOD PYTHON_METHOD_DEF \ ( \ - unpack_uleb128, "pbuf", \ + unpack_uleb128, "buf", \ METH_VARARGS, py_leb128, \ - "Unpack an unsigned LEB128 value into a data buffer.\n" \ + "Unpack an unsigned LEB128 value from bytes.\n" \ "\n" \ - "The *pbuf* argument has to be a" \ - " pychrysalide.common.PackedBuffer instance from where data" \ - " will be read.\n" \ + "The *buf* argument needs to be bytes with enough data aimed" \ + " to get translated into an unsigned LEB128 value.\n" \ "\n" \ "The returned value depends on the operation status: *None*" \ - " for failure or a integer value for success." \ + " for failure or a tuple with two items for success: the" \ + " decoded value and the remaining bytes." \ ) - ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "y#", &bytes, &count); if (!ret) return NULL; - status = unpack_uleb128(&value, pbuf); + pos = bytes; + max = bytes + count; - if (status) - result = PyLong_FromUnsignedLongLong(value); + status = unpack_uleb128(&value, &pos, max); + if (status) + { + result = PyTuple_New(2); + PyTuple_SetItem(result, 0, PyLong_FromUnsignedLongLong(value)); + PyTuple_SetItem(result, 1, PyBytes_FromStringAndSize(pos, (char *)max - (char *)pos)); + } else { result = Py_None; @@ -218,33 +223,43 @@ static PyObject *py_leb128_unpack_uleb128(PyObject *self, PyObject *args) static PyObject *py_leb128_unpack_leb128(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + const char *bytes; /* Octets brutes transmis */ + Py_ssize_t count; /* Quantité de ces octets */ int ret; /* Bilan de lecture des args. */ + const void *pos; /* Tëte de lecture */ + const void *max; /* Position de lecture maximale*/ leb128_t value; /* Valeur à manipuler */ bool status; /* Bilan de l'opération */ #define LEB128_UNPACK_LEB128_METHOD PYTHON_METHOD_DEF \ ( \ - unpack_leb128, "pbuf", \ + unpack_leb128, "buf", \ METH_VARARGS, py_leb128, \ - "Unpack a signed LEB128 value into a data buffer.\n" \ + "Unpack a signed LEB128 value from bytes.\n" \ "\n" \ - "The *pbuf* argument has to be a" \ - " pychrysalide.common.PackedBuffer instance from where data" \ - " will be read.\n" \ + "\n" \ + "The *buf* argument needs to be bytes with enough data aimed" \ + " to get translated into a signed LEB128 value.\n" \ "\n" \ "The returned value depends on the operation status: *None*" \ - " for failure or a integer value for success." \ + " for failure or a tuple with two items for success: the" \ + " decoded value and the remaining bytes." \ ) - ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "y#", &bytes, &count); if (!ret) return NULL; - status = unpack_leb128(&value, pbuf); + pos = bytes; + max = bytes + count; - if (status) - result = PyLong_FromLongLong(value); + status = unpack_leb128(&value, &pos, max); + if (status) + { + result = PyTuple_New(2); + PyTuple_SetItem(result, 0, PyLong_FromLongLong(value)); + PyTuple_SetItem(result, 1, PyBytes_FromStringAndSize(pos, (char *)max - (char *)pos)); + } else { result = Py_None; diff --git a/plugins/pychrysalide/common/module.c b/plugins/pychrysalide/common/module.c index a0042ee..c82c7bc 100644 --- a/plugins/pychrysalide/common/module.c +++ b/plugins/pychrysalide/common/module.c @@ -26,13 +26,15 @@ #include "bits.h" -#include "fnv1a.h" -#include "hex.h" -#include "itoa.h" +#include "entropy.h" +//#include "fnv1a.h" +//#include "hex.h" +//#include "itoa.h" #include "leb128.h" -#include "packed.h" -#include "pathname.h" -#include "pearson.h" +//#include "packed.h" +//#include "pathname.h" +//#include "pearson.h" +#include "xdg.h" #include "../helpers.h" @@ -98,15 +100,19 @@ bool populate_common_module(void) result = true; + /* if (result) result = populate_common_module_with_fnv1a(); if (result) result = populate_common_module_with_hex(); if (result) result = populate_common_module_with_itoa(); - if (result) result = populate_common_module_with_leb128(); if (result) result = populate_common_module_with_pathname(); if (result) result = populate_common_module_with_pearson(); + */ + if (result) result = populate_common_module_with_entropy(); + if (result) result = populate_common_module_with_leb128(); + if (result) result = populate_common_module_with_xdg(); if (result) result = ensure_python_bitfield_is_registered(); - if (result) result = ensure_python_packed_buffer_is_registered(); + //if (result) result = ensure_python_packed_buffer_is_registered(); assert(result); diff --git a/plugins/pychrysalide/common/xdg.c b/plugins/pychrysalide/common/xdg.c new file mode 100644 index 0000000..e4b269e --- /dev/null +++ b/plugins/pychrysalide/common/xdg.c @@ -0,0 +1,384 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * xdg.c - équivalent Python du fichier "common/xdg.c" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "xdg.h" + + +#include <malloc.h> + + +#include <common/xdg.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* Détermine le chemin d'un répertoire de données XDG. */ +static PyObject *py_xdg_get_xdg_cache_dir(PyObject *, PyObject *); + +/* Détermine le chemin d'un répertoire de données XDG. */ +static PyObject *py_xdg_get_xdg_config_dir(PyObject *, PyObject *); + +/* Détermine le chemin d'un répertoire de données XDG. */ +static PyObject *py_xdg_get_xdg_data_dir(PyObject *, PyObject *); + +/* Détermine le chemin d'un répertoire de données XDG. */ +static PyObject *py_xdg_get_xdg_state_dir(PyObject *, PyObject *); + +/* Détermine le chemin d'un répertoire éphémère XDG. */ +static PyObject *py_xdg_get_xdg_runtime_dir(PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = arguments fournis lors de l'appel à la fonction. * +* * +* Description : Détermine le chemin d'un répertoire de données XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_xdg_get_xdg_cache_dir(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + int create; /* Besoin en création */ + const char *suffix; /* Fin de la localisation */ + int ret; /* Bilan de lecture des args. */ + char *filename; /* Chemin d'accès construit */ + +#define GET_XDG_CACHE_DIR_METHOD PYTHON_METHOD_DEF \ +( \ + get_xdg_cache_dir, "suffix, create=True", \ + METH_VARARGS, py_xdg, \ + "Get the location of a file belonging to the base directory" \ + " pointed by the *XDG_CACHE_HOME* environment variable.\n" \ + "\n" \ + "The *suffix* argument is a string appended to the XDG base" \ + " directory. The *create* option ensures all the directories" \ + " involved in the returned path exist.\n" \ + "\n" \ + "The function returns the full filename to use for a content" \ + " related to cache according to the XDG specifications, or" \ + " *None* in case of failure." \ +) + + create = 1; + + ret = PyArg_ParseTuple(args, "s|p", &suffix, &create); + if (!ret) return NULL; + + filename = get_xdg_cache_dir(suffix, create); + + if (filename == NULL) + { + result = Py_None; + Py_INCREF(result); + } + else + { + result = PyUnicode_FromString(filename); + free(filename); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = arguments fournis lors de l'appel à la fonction. * +* * +* Description : Détermine le chemin d'un répertoire de données XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_xdg_get_xdg_config_dir(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + int create; /* Besoin en création */ + const char *suffix; /* Fin de la localisation */ + int ret; /* Bilan de lecture des args. */ + char *filename; /* Chemin d'accès construit */ + +#define GET_XDG_CONFIG_DIR_METHOD PYTHON_METHOD_DEF \ +( \ + get_xdg_config_dir, "suffix, create=True", \ + METH_VARARGS, py_xdg, \ + "Get the location of a file belonging to the base directory" \ + " pointed by the *XDG_CONFIG_HOME* environment variable.\n" \ + "\n" \ + "The *suffix* argument is a string appended to the XDG base" \ + " directory. The *create* option ensures all the directories" \ + " involved in the returned path exist.\n" \ + "\n" \ + "The function returns the full filename to use for a content" \ + " related to configuration according to the XDG specifications,"\ + " or *None* in case of failure." \ +) + + create = 1; + + ret = PyArg_ParseTuple(args, "s|p", &suffix, &create); + if (!ret) return NULL; + + filename = get_xdg_config_dir(suffix, create); + + if (filename == NULL) + { + result = Py_None; + Py_INCREF(result); + } + else + { + result = PyUnicode_FromString(filename); + free(filename); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = arguments fournis lors de l'appel à la fonction. * +* * +* Description : Détermine le chemin d'un répertoire de données XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_xdg_get_xdg_data_dir(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + int create; /* Besoin en création */ + const char *suffix; /* Fin de la localisation */ + int ret; /* Bilan de lecture des args. */ + char *filename; /* Chemin d'accès construit */ + +#define GET_XDG_DATA_DIR_METHOD PYTHON_METHOD_DEF \ +( \ + get_xdg_data_dir, "suffix, create=True", \ + METH_VARARGS, py_xdg, \ + "Get the location of a file belonging to the base directory" \ + " pointed by the *XDG_DATA_HOME* environment variable.\n" \ + "\n" \ + "The *suffix* argument is a string appended to the XDG base" \ + " directory. The *create* option ensures all the directories" \ + " involved in the returned path exist.\n" \ + "\n" \ + "The function returns the full filename to use for a content" \ + " related to data according to the XDG specifications, or" \ + " *None* in case of failure." \ +) + + create = 1; + + ret = PyArg_ParseTuple(args, "s|p", &suffix, &create); + if (!ret) return NULL; + + filename = get_xdg_data_dir(suffix, create); + + if (filename == NULL) + { + result = Py_None; + Py_INCREF(result); + } + else + { + result = PyUnicode_FromString(filename); + free(filename); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = arguments fournis lors de l'appel à la fonction. * +* * +* Description : Détermine le chemin d'un répertoire de données XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_xdg_get_xdg_state_dir(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + int create; /* Besoin en création */ + const char *suffix; /* Fin de la localisation */ + int ret; /* Bilan de lecture des args. */ + char *filename; /* Chemin d'accès construit */ + +#define GET_XDG_STATE_DIR_METHOD PYTHON_METHOD_DEF \ +( \ + get_xdg_state_dir, "suffix, create=True", \ + METH_VARARGS, py_xdg, \ + "Get the location of a file belonging to the base directory" \ + " pointed by the *XDG_STATE_HOME* environment variable.\n" \ + "\n" \ + "The *suffix* argument is a string appended to the XDG base" \ + " directory. The *create* option ensures all the directories" \ + " involved in the returned path exist.\n" \ + "\n" \ + "The function returns the full filename to use for a content" \ + " related to states according to the XDG specifications, or" \ + " *None* in case of failure." \ +) + + create = 1; + + ret = PyArg_ParseTuple(args, "s|p", &suffix, &create); + if (!ret) return NULL; + + filename = get_xdg_state_dir(suffix, create); + + if (filename == NULL) + { + result = Py_None; + Py_INCREF(result); + } + else + { + result = PyUnicode_FromString(filename); + free(filename); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = arguments fournis lors de l'appel à la fonction. * +* * +* Description : Détermine le chemin d'un répertoire éphémère XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_xdg_get_xdg_runtime_dir(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + const char *suffix; /* Fin de la localisation */ + int ret; /* Bilan de lecture des args. */ + char *filename; /* Chemin d'accès construit */ + +#define GET_XDG_RUNTIME_DIR_METHOD PYTHON_METHOD_DEF \ +( \ + get_xdg_runtime_dir, "suffix", \ + METH_VARARGS, py_xdg, \ + "Get the location of a file belonging to the base directory" \ + " pointed by the *XDG_RUNTIME_DIR* environment variable.\n" \ + "\n" \ + "The *suffix* argument is a string appended to the XDG base" \ + " directory.\n" \ + "\n" \ + "The function returns the full filename to use for a content" \ + " related to runtime data according to the XDG specifications," \ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s", &suffix); + if (!ret) return NULL; + + filename = get_xdg_runtime_dir(suffix); + + if (filename == NULL) + { + result = Py_None; + Py_INCREF(result); + } + else + { + result = PyUnicode_FromString(filename); + free(filename); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Définit une extension du module 'common' à compléter. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_common_module_with_xdg(void) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Module à recompléter */ + + static PyMethodDef py_xdg_methods[] = { + GET_XDG_CACHE_DIR_METHOD, + GET_XDG_CONFIG_DIR_METHOD, + GET_XDG_DATA_DIR_METHOD, + GET_XDG_STATE_DIR_METHOD, + GET_XDG_RUNTIME_DIR_METHOD, + { NULL } + }; + + module = get_access_to_python_module("pychrysalide.common"); + + result = register_python_module_methods(module, py_xdg_methods); + + return result; + +} diff --git a/plugins/pychrysalide/plugins/constants.h b/plugins/pychrysalide/common/xdg.h index 56612c9..f1aa16a 100644 --- a/plugins/pychrysalide/plugins/constants.h +++ b/plugins/pychrysalide/common/xdg.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * constants.h - prototypes pour l'ajout des constantes principales + * xdg.h - prototypes pour l'équivalent Python du fichier "common/xdg.c" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,20 +22,18 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H -#define _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H +#ifndef _PLUGINS_PYCHRYSALIDE_COMMON_XDG_H +#define _PLUGINS_PYCHRYSALIDE_COMMON_XDG_H #include <Python.h> #include <stdbool.h> -/* Définit les constantes relatives aux greffons Python. */ -bool define_plugin_module_constants(PyTypeObject *); -/* Tente de convertir en constante PluginAction. */ -int convert_to_plugin_action(PyObject *, void *); +/* Définit une extension du module 'common' à compléter. */ +bool populate_common_module_with_xdg(void); -#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_COMMON_XDG_H */ diff --git a/plugins/pychrysalide/constants.c b/plugins/pychrysalide/constants.c index 8142284..34caa12 100644 --- a/plugins/pychrysalide/constants.c +++ b/plugins/pychrysalide/constants.c @@ -25,7 +25,6 @@ #include "constants.h" -#include <arch/archbase.h> #include <common/datatypes.h> diff --git a/plugins/pychrysalide/constants.h b/plugins/pychrysalide/constants.h index 332afe0..151f1eb 100644 --- a/plugins/pychrysalide/constants.h +++ b/plugins/pychrysalide/constants.h @@ -46,6 +46,9 @@ int convert_to_source_endian(PyObject *, void *); /* Tente de convertir en constante MemoryDataSize. */ int convert_to_memory_data_size(PyObject *, void *); +#define cast_memory_data_size_to_python(v) \ + cast_with_constants_group_from_module("pychrysalide", "MemoryDataSize", v) + #endif /* _PLUGINS_PYCHRYSALIDE_CONSTANTS_H */ diff --git a/plugins/pychrysalide/convert.c b/plugins/pychrysalide/convert.c new file mode 100644 index 0000000..08866cb --- /dev/null +++ b/plugins/pychrysalide/convert.c @@ -0,0 +1,120 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * convert.c - conversion d'arguments en éléments usuels externes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "convert.h" + + +#include <assert.h> +#include <pygobject.h> +#ifndef NDEBUG +# include <stdbool.h> +#endif +#include <gio/gio.h> + + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en instance GSettings. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_gsettings(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + GType type; /* Type obtenu ou 0 */ + + result = PyObject_IsInstance(arg, (PyObject *)&PyGObject_Type); + + if (result == 1) + { + type = pyg_type_from_object(arg); + + if (type != G_TYPE_SETTINGS) + result = 0; + + } + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GSetting instance"); + break; + + case 1: + *((GSettings **)dst) = G_SETTINGS(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en instance GSettings ou NULL. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_gsettings_or_none(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + if (arg == Py_None) + { + *((GSettings **)dst) = NULL; + result = 1; + } + + else + result = convert_to_gsettings(arg, dst); + + return result; + +} diff --git a/plugins/pychrysalide/convert.h b/plugins/pychrysalide/convert.h new file mode 100644 index 0000000..86d7528 --- /dev/null +++ b/plugins/pychrysalide/convert.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * convert.h - prototypes pour la conversion d'arguments en éléments usuels externes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_CONVERT_H +#define _PLUGINS_PYCHRYSALIDE_CONVERT_H + + +#include <Python.h> + + + +/* Tente de convertir en instance GSettings. */ +int convert_to_gsettings(PyObject *, void *); + +/* Tente de convertir en instance GSettings ou NULL. */ +int convert_to_gsettings_or_none(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CONVERT_H */ diff --git a/plugins/pychrysalide/core-int.h b/plugins/pychrysalide/core-int.h new file mode 100644 index 0000000..2b8fcc8 --- /dev/null +++ b/plugins/pychrysalide/core-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-int.h - prototypes internes pour le plugin permettant des extensions en Python + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_CORE_INT_H +#define _PLUGINS_PYCHRYSALIDE_CORE_INT_H + + +#include "core.h" + + +#include <plugins/native-int.h> + + + +/* Greffon natif pour la liaison Python de Chrysalide (instance) */ +struct _GPyChrysalidePlugin +{ + GNativePlugin parent; /* A laisser en premier */ + + PyObject *py_module; /* Réceptacle de chargement */ + +}; + + +/* Greffon natif pour la liaison Python de Chrysalide (classe) */ +struct _GPyChrysalidePluginClass +{ + GNativePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un greffon de support Python. */ +bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *, GModule *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_INT_H */ diff --git a/plugins/pychrysalide/core-ui-int.h b/plugins/pychrysalide/core-ui-int.h new file mode 100644 index 0000000..7cecc13 --- /dev/null +++ b/plugins/pychrysalide/core-ui-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui-int.h - prototypes internes pour le plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H +#define _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H + + +#include "core-ui.h" + + +#include "core-int.h" + + + +/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (instance) */ +struct _GPyChrysalidePluginUI +{ + GPyChrysalidePlugin parent; /* A laisser en premier */ + +}; + + +/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (classe) */ +struct _GPyChrysalidePluginUIClass +{ + GPyChrysalidePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un greffon de support Python. */ +bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *, GModule *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H */ diff --git a/plugins/pychrysalide/core-ui.c b/plugins/pychrysalide/core-ui.c new file mode 100644 index 0000000..00d1cc1 --- /dev/null +++ b/plugins/pychrysalide/core-ui.c @@ -0,0 +1,499 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.c - plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "core-ui.h" + + +#include <stdbool.h> + + +#include <i18n.h> +#ifdef DISCARD_LOCAL +# include <core/paths.h> +#endif +#include <plugins/manager-int.h> +#include <plugins/self.h> + + +#include "bindings.h" +#include "core-ui-int.h" +#include "arch/module-ui.h" +#include "glibext/module-ui.h" +#include "gtkext/module.h" +//#include "gui/module.h" + + + +/* Note la nature du chargement */ +static bool _standalone = true; + + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +/* Initialise la classe des greffons de support Python. */ +static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *); + +/* Procède à l'initialisation de l'interface de gestion. */ +static void g_pychrysalide_plugin_ui_plugin_manager_interface_init(GPluginManagerInterface *); + +/* Initialise une instance de greffon de support Python. */ +static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *); + +/* Supprime toutes les références externes. */ +static void g_pychrysalide_plugin_ui_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_pychrysalide_plugin_ui_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_pychrysalide_plugin_ui_enable(GPluginModule *); + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Prend acte du chargement de l'ensemble des greffons natifs. */ +static void g_pychrysalide_plugin_ui_handle_native_plugins_loaded_event(GPluginManager *); + + + +/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */ + + +/*Ajoute des modules UI aux extensions Python. */ +static bool add_python_ui_modules(PyObject *); + +/* Inscrit les défintions des objets UI Python de Chrysalide. */ +static bool populate_python_modules_ui(bool); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon de liaison Python */ +G_DEFINE_TYPE_WITH_CODE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G_TYPE_PYCHRYSALIDE_PLUGIN, + G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pychrysalide_plugin_ui_plugin_manager_interface_init)); + + +NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_ui_new); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons de support Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = g_pychrysalide_plugin_ui_dispose; + object->finalize = g_pychrysalide_plugin_ui_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = g_pychrysalide_plugin_ui_enable; + +} + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de gestion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_plugin_manager_interface_init(GPluginManagerInterface *iface) +{ + iface->handle_native = g_pychrysalide_plugin_ui_handle_native_plugins_loaded_event; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon de support Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *plugin) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_dispose(GObject *object) +{ + G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_finalize(GObject *object) +{ + G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : module = extension vue du système. * +* * +* Description : Crée un module pour un greffon de support Python. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +GPluginModule *g_pychrysalide_plugin_ui_new(GModule *module) +{ + GPyChrysalidePluginUI *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN_UI, NULL); + + if (!g_pychrysalide_plugin_ui_create(result, module)) + g_clear_object(&result); + + return G_PLUGIN_MODULE(result); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = extension vue du système. * +* * +* Description : Met en place un module pour un greffon de support Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *plugin, GModule *module) +{ + bool result; /* Bilan à retourner */ + + result = g_pychrysalide_plugin_create(G_PYCHRYSALIDE_PLUGIN(plugin), module); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de l'activation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_pychrysalide_plugin_ui_enable(GPluginModule *plugin) +{ + bool result; /* Bilan à retourner */ + GPyChrysalidePlugin *pychr_plugin; /* Version spécialisée */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + int ret; /* Bilan de préparatifs */ + + _standalone = false; + + pychr_plugin = G_PYCHRYSALIDE_PLUGIN(plugin); + + /* Chargement du module pour Python */ + + ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalideui); + + if (ret == -1) + { + g_plugin_module_log_simple_message(plugin, LMT_ERROR, + _("Can not extend the existing table of Python built-in modules.")); + + result = false; + goto done; + + } + + Py_Initialize(); + + gstate = PyGILState_Ensure(); + + pychr_plugin->py_module = PyImport_ImportModule("pychrysalide"); + + /** + * Pour mémoire, une situation concrête conduisant à un échec : + * le paquet python3-gi-dbg n'est pas installé alors que le + * programme est compilé en mode débogage. + * + * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() + * le laisse rien filtrer... + * + * En mode autonome, le shell Python remonte bien l'erreur par contre. + */ + + // TODO : check (2025) + + result = (pychr_plugin->py_module != NULL); + + PyGILState_Release(gstate); + + done: + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : manager = interface à manipuler. * +* * +* Description : Accompagne la fin du chargement des modules natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_handle_native_plugins_loaded_event(GPluginManager *manager) +{ + GPluginModule *plugin; /* Version de base du greffon */ +#ifdef DISCARD_LOCAL + char *edir; /* Répertoire de base effectif */ + DIR *dir; /* Répertoire à parcourir */ +#endif + GPluginManagerInterface *iface; /* Interface utilisée */ + GPluginManagerInterface *parent_iface; /* Interface parente */ + + plugin = G_PLUGIN_MODULE(manager); + + /* Définition des zones d'influence */ + +#ifndef DISCARD_LOCAL + + extend_python_path(plugin, PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" G_DIR_SEPARATOR_S "pythonui"); + +#else + + edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "pythonui"); + + dir = opendir(edir); + + if (dir != NULL) + { + closedir(dir); + + extend_python_path(plugin, edir); + + } + + free(edir); + +#endif + + /* Chargements des extensions Python */ + + iface = G_PLUGIN_MANAGER_GET_IFACE(manager); + + parent_iface = g_type_interface_peek_parent(iface); + + parent_iface->handle_native(manager); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* POINT D'ENTREE POUR PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : super = module dont la définition est à compléter. * +* * +* Description : Ajoute des modules UI aux extensions Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool add_python_ui_modules(PyObject *super) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = add_gtkext_module(super); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : modify = autorisation de motification du type GObject. * +* * +* Description : Inscrit les défintions des objets UI Python de Chrysalide. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool populate_python_modules_ui(bool modify) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (!modify) + { + if (result) result = populate_arch_module_ui(); + if (result) result = populate_glibext_module_ui(); + + } + + else + { + if (result) result = populate_gtkext_module(); + //if (result) result = populate_gui_module(); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Point d'entrée pour l'initialisation de Python. * +* * +* Retour : ? * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyMODINIT_FUNC PyInit_pychrysalideui(void) +{ + PyObject *result; /* Module Python à retourner */ + pyinit_details_t details; /* Détails de chargement */ + + details.standalone = _standalone; + + details.add_extra = add_python_ui_modules; + details.populate_extra = populate_python_modules_ui; + details.create_self = g_pychrysalide_plugin_ui_new; + + result = init_python_pychrysalide_module(&details); + + return result; + +} diff --git a/plugins/pychrysalide/core-ui.h b/plugins/pychrysalide/core-ui.h new file mode 100644 index 0000000..658aa19 --- /dev/null +++ b/plugins/pychrysalide/core-ui.h @@ -0,0 +1,64 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.h - prototypes pour le plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_CORE_UI_H +#define _PLUGINS_PYCHRYSALIDE_CORE_UI_H + + +/** + * Note: + * Since Python may define some pre-processor definitions which affect the standard headers + * on some systems, you must include Python.h before any standard headers are included. + * + * cf. https://docs.python.org/3.4/c-api/intro.html + */ +#include <Python.h> + + +#include <glibext/helpers.h> +#include <plugins/plugin.h> + + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +#define G_TYPE_PYCHRYSALIDE_PLUGIN_UI (g_pychrysalide_plugin_ui_get_type()) + +DECLARE_GTYPE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G, PYCHRYSALIDE_PLUGIN_UI); + + +/* Crée un module pour un greffon de support Python. */ +GPluginModule *g_pychrysalide_plugin_ui_new(GModule *); + + + +/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */ + + +/* Point d'entrée pour l'initialisation de Python. */ +PyMODINIT_FUNC PyInit_pychrysalideui(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_H */ diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c index 35c0e35..0fea9c4 100644 --- a/plugins/pychrysalide/core.c +++ b/plugins/pychrysalide/core.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.c - plugin permettant des extensions en Python * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,20 +21,18 @@ */ +#include "core.h" + + #undef NO_IMPORT_PYGOBJECT #include <pygobject.h> #define NO_IMPORT_PYGOBJECT -#include "core.h" - - #include <assert.h> #include <errno.h> #include <malloc.h> -#include <pygobject.h> #include <stdarg.h> -#include <stdio.h> #include <stdbool.h> #include <string.h> #include <unistd.h> @@ -42,768 +40,467 @@ #include <i18n.h> #include <gleak.h> -#include <common/cpp.h> #include <common/environment.h> #include <common/extstr.h> #include <core/core.h> #include <core/logs.h> #include <core/paths.h> -#include <plugins/pglist.h> +#include <plugins/manager-int.h> +#include <plugins/plugin.h> #include <plugins/self.h> #include "access.h" -#include "constants.h" -#include "helpers.h" -#include "star.h" -#include "strenum.h" -#include "struct.h" -#include "analysis/module.h" -#include "arch/module.h" -/* #include "common/module.h" */ -/* #include "core/module.h" */ -/* #include "debug/module.h" */ -/* #include "format/module.h" */ -/* #include "glibext/module.h" */ -/* #ifdef INCLUDE_GTK_SUPPORT */ -/* # include "gtkext/module.h" */ -/* # include "gui/module.h" */ -/* #endif */ -/* #include "mangling/module.h" */ -#include "plugins/module.h" -#include "plugins/plugin.h" - - - -DEFINE_CHRYSALIDE_CONTAINER_PLUGIN("PyChrysalide", "Chrysalide bindings to Python", - PACKAGE_VERSION, CHRYSALIDE_WEBSITE("api/python/pychrysalide"), - NO_REQ, AL(PGA_PLUGIN_INIT, PGA_PLUGIN_EXIT, - PGA_NATIVE_PLUGINS_LOADED, PGA_TYPE_BUILDING)); +#include "bindings.h" +#include "core-int.h" + /* Note la nature du chargement */ static bool _standalone = true; -/* Réceptacle pour le chargement forcé */ -static PyObject *_chrysalide_module = NULL; -/* Fournit la révision du programme global. */ -static PyObject *py_chrysalide_revision(PyObject *, PyObject *); +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ -/* Fournit la version du programme global. */ -static PyObject *py_chrysalide_version(PyObject *, PyObject *); -/* Fournit la version du greffon pour Python. */ -static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *); +/* Initialise la classe des greffons de support Python. */ +static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *); -/* Détermine si l'interpréteur lancé est celui pris en compte. */ -static bool is_current_abi_suitable(void); +/* Procède à l'initialisation de l'interface de gestion. */ +static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *); -/* Assure une pleine initialisation des objets de Python-GI. */ -static bool install_metaclass_for_python_gobjects(void); +/* Initialise une instance de greffon de support Python. */ +static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *); -/* Définit la version attendue de GTK à charger dans Python. */ -#ifdef INCLUDE_GTK_SUPPORT -static bool set_version_for_gtk_namespace(const char *); -#endif +/* Supprime toutes les références externes. */ +static void g_pychrysalide_plugin_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_pychrysalide_plugin_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_pychrysalide_plugin_enable(GPluginModule *); -/* Point de sortie pour l'initialisation de Python. */ -static void PyExit_pychrysalide(void); +/* Prend acte de la désactivation du greffon. */ +static bool g_pychrysalide_plugin_disable(GPluginModule *); -/* Complète les chemins de recherches de Python. */ -static void extend_python_path(const char *); + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Crée un greffon à partir de code Python. */ +static GPluginModule *create_python_plugin(const char *, const char *); /* Charge autant de greffons composés en Python que possible. */ static void load_python_plugins(GPluginModule *); -/* Efface un type Python pour greffon de la mémoire. */ -static void free_native_plugin_type(PyTypeObject *); +/* Prend acte du chargement de l'ensemble des greffons natifs. */ +static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPluginManager *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ +/* Indique le type défini pour un greffon de liaison Python */ +G_DEFINE_TYPE_WITH_CODE(GPyChrysalidePlugin, g_pychrysalide_plugin, G_TYPE_NATIVE_PLUGIN, + G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pychrysalide_plugin_plugin_manager_interface_init)); + + +NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_new); + /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : class = classe à initialiser. * * * -* Description : Fournit la révision du programme global. * +* Description : Initialise la classe des greffons de support Python. * * * -* Retour : Numéro de révision. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *class) { - PyObject *result; /* Valeur à retourner */ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ -#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \ -( \ - revision, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the revision number of Chrysalide.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: 'r1665'." \ -) + object = G_OBJECT_CLASS(class); - result = PyUnicode_FromString("r" XSTR(REVISION)); + object->dispose = g_pychrysalide_plugin_dispose; + object->finalize = g_pychrysalide_plugin_finalize; - return result; + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = g_pychrysalide_plugin_enable; + plugin->disable = g_pychrysalide_plugin_disable; } /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Fournit la version du programme global. * +* Description : Procède à l'initialisation de l'interface de gestion. * * * -* Retour : Numéro de version. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_version(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface) { - PyObject *result; /* Valeur à retourner */ - int major; /* Numéro de version majeur */ - int minor; /* Numéro de version mineur */ - int revision; /* Numéro de révision */ - char version[16]; /* Conservation temporaire */ + iface->handle_native = g_pychrysalide_plugin_handle_native_plugins_loaded_event; -#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \ -( \ - version, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the version number of Chrysalide.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: '1.6.65'." \ -) +} - major = REVISION / 1000; - minor = (REVISION - (major * 1000)) / 100; - revision = REVISION % 100; - snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision); +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon de support Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - result = PyUnicode_FromString(version); +static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *plugin) +{ + STORE_PLUGIN_ABI(plugin); - return result; + plugin->py_module = NULL; } /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Fournit la version du greffon pour Python. * +* Description : Supprime toutes les références externes. * * * -* Retour : Numéro de version. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_dispose(GObject *object) { - PyObject *result; /* Valeur à retourner */ - char version[16]; /* Conservation temporaire */ - -#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \ -( \ - mod_version, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the version number of Chrysalide module for Python.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: '0.1.0'." \ -) - - snprintf(version, sizeof(version), "%s", _chrysalide_plugin.version); - - result = PyUnicode_FromString(version); - - return result; + G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Détermine si l'interpréteur lancé est celui pris en compte. * +* Description : Procède à la libération totale de la mémoire. * * * -* Retour : true si l'exécution peut se poursuivre, false sinon. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool is_current_abi_suitable(void) +static void g_pychrysalide_plugin_finalize(GObject *object) { - bool result; - int fds[2]; - int ret; - char cmds[128]; - char content[64]; - ssize_t got; - -#define GRAB_ABI_FLAGS_IN_PYTHON \ - "import sys" "\n" \ - "import os" "\n" \ - "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \ - "os.write(%d, data)" "\n" - - result = false; - - ret = pipe(fds); - if (ret == -1) - { - perror("pipe()"); - return false; - } + G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->finalize(object); - snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]); +} - ret = PyRun_SimpleString(cmds); - if (ret != 0) goto icas_exit; - got = read(fds[0], content, sizeof(content)); - if (got < 0) - { - perror("read()"); - goto icas_exit; - } - - content[got] = '\0'; +/****************************************************************************** +* * +* Paramètres : module = extension vue du système. * +* * +* Description : Crée un module pour un greffon de support Python. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ - result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0); +GPluginModule *g_pychrysalide_plugin_new(GModule *module) +{ + GPyChrysalidePlugin *result; /* Structure à retourner */ - icas_exit: + result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN, NULL); - if (!result) - PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \ - "the ones of the Python library used during the module compilation."); + if (!g_pychrysalide_plugin_create(result, module)) + g_clear_object(&result); - return result; + return G_PLUGIN_MODULE(result); } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = extension vue du système. * * * -* Description : Assure une pleine initialisation des objets de Python-GI. * +* Description : Met en place un module pour un greffon de support Python. * * * * Retour : Bilan de l'opération. * * * -* Remarques : - * +* Remarques : Le transfert de propriétée du module est total. * * * ******************************************************************************/ -static bool install_metaclass_for_python_gobjects(void) +bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *plugin, GModule *module) { bool result; /* Bilan à retourner */ - PyObject *gi_types_mod; /* Module Python-GObject */ - - /** - * Les extensions Python sont chargées à partir de la fonction load_python_plugins(), - * qui fait appel à create_python_plugin(). Une instance y est construite via un - * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad - * dans le fichier __init__.py présent dans chaque module d'extension. - * - * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique - * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction - * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c. - * Le code de cette dernière comprend notamment la portion suivante : - * - * [...] - * Py_SET_TYPE(type, PyGObject_MetaType); - * [...] - * if (PyType_Ready(type) < 0) { - * g_warning ("couldn't make the type `%s' ready", type->tp_name); - * return; - * } - * [...] - * - * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c - * et commence par : - * - * int PyType_Ready(PyTypeObject *type) - * { - * if (type->tp_flags & Py_TPFLAGS_READY) { - * assert(_PyType_CheckConsistency(type)); - * return 0; - * } - * [...] - * } - * - * La vérification de cohérencce commence par analyser le type et son propre - * type : - * - * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c : - * - * int _PyType_CheckConsistency(PyTypeObject *type) - * { - * [...] - * CHECK(!_PyObject_IsFreed((PyObject *)type)); - * [...] - * } - * - * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c : - * - * int _PyObject_IsFreed(PyObject *op) - * { - * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) { - * return 1; - * } - * - * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL. - * - * Or le type du type est écrasé dans la fonction pygobject_register_class() - * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est - * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c : - * - * static PyObject * - * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass) - * { - * Py_INCREF(metaclass); - * PyGObject_MetaType = metaclass; - * Py_INCREF(metaclass); - * - * Py_SET_TYPE(&PyGObject_Type, metaclass); - * - * Py_INCREF(Py_None); - * return Py_None; - * } - * - * Afin de valider la vérification de _PyType_CheckConsistency() pour les - * modules externes qui entraînent un enregistrement tout en portant le drapeau - * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut - * initialiser au besoin la variable PyGObject_MetaType. - * - * Une ligne suffit donc à enregistrer le type intermédiaire : - * - * from _gi import types - * - * On simule ici une déclaration similaire si nécessaire - */ - - result = false; - if (PyType_CheckExact(&PyGObject_Type)) - { - gi_types_mod = PyImport_ImportModule("gi.types"); + result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin), + "PyChrysalide", + "Chrysalide bindings to Python", + PACKAGE_VERSION, + CHRYSALIDE_WEBSITE("api/python/pychrysalide"), + NO_REQ, + module); - result = (PyErr_Occurred() == NULL); + return result; - if (result) - result = (PyType_CheckExact(&PyGObject_Type) == 0); +} - Py_XDECREF(gi_types_mod); - } - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : version = idenfiant de la version de GTK à stipuler. * +* Paramètres : plugin = greffon à manipuler. * * * -* Description : Définit la version attendue de GTK à charger dans Python. * +* Description : Prend acte de l'activation du greffon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -#ifdef INCLUDE_GTK_SUPPORT -static bool set_version_for_gtk_namespace(const char *version) + +static bool g_pychrysalide_plugin_enable(GPluginModule *plugin) { bool result; /* Bilan à retourner */ - PyObject *gi_mod; /* Module Python-GObject */ - PyObject *args; /* Arguments à fournir */ - - result = false; + GPyChrysalidePlugin *pychr_plugin; /* Version spécialisée */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + int ret; /* Bilan de préparatifs */ - /** - * On cherche ici à éviter le message suivant si on charge 'gi.repository.Gtk' directement : - * - * - * PyGIWarning: Gtk was imported without specifying a version first. \ - * Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded. - * - */ + _standalone = false; - gi_mod = PyImport_ImportModule("gi"); + pychr_plugin = G_PYCHRYSALIDE_PLUGIN(plugin); - if (gi_mod != NULL) - { - args = Py_BuildValue("ss", "Gtk", version); + /* Chargement du module pour Python */ - run_python_method(gi_mod, "require_version", args); + ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide); - result = (PyErr_Occurred() == NULL); + if (ret == -1) + { + g_plugin_module_log_simple_message(plugin, LMT_ERROR, + _("Can not extend the existing table of Python built-in modules.")); - Py_DECREF(args); - Py_DECREF(gi_mod); + result = false; + goto done; } - return result; - -} -#endif - + Py_Initialize(); -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Point de sortie pour l'initialisation de Python. * -* * -* Retour : ? * -* * -* Remarques : - * -* * -******************************************************************************/ + gstate = PyGILState_Ensure(); -static void PyExit_pychrysalide(void) -{ - assert(_standalone); + pychr_plugin->py_module = PyImport_ImportModule("pychrysalide"); - /* - extern void set_current_project(void *project); + /** + * Pour mémoire, une situation concrête conduisant à un échec : + * le paquet python3-gi-dbg n'est pas installé alors que le + * programme est compilé en mode débogage. + * + * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() + * le laisse rien filtrer... + * + * En mode autonome, le shell Python remonte bien l'erreur par contre. + */ - set_current_project(NULL); - */ + // TODO : check (2025) -#ifdef TRACK_GOBJECT_LEAKS - remember_gtypes_for_leaks(); -#endif + result = (pychr_plugin->py_module != NULL); - exit_all_plugins(); + PyGILState_Release(gstate); - //unload_all_core_components(true); + done: -#ifdef TRACK_GOBJECT_LEAKS - dump_remaining_gtypes(); -#endif + return result; } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : plugin = greffon à manipuler. * * * -* Description : Point d'entrée pour l'initialisation de Python. * +* Description : Prend acte de la désactivation du greffon. * * * -* Retour : ? * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -#define PYCHRYSALIDE_DOC \ - "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \ - "\n" \ - "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \ - "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \ - "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \ - "\n" \ - "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \ - "how the API works.\n" \ - "\n" \ - "These plugins are located in the 'plugins/python' directory.\n" \ - "\n" \ - "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup." - -PyMODINIT_FUNC PyInit_pychrysalide(void) +static bool g_pychrysalide_plugin_disable(GPluginModule *plugin) { - PyObject *result; /* Module Python à retourner */ - bool status; /* Bilan des inclusions */ - int ret; /* Bilan de préparatifs */ -#ifdef PYTHON_PACKAGE - Dl_info info; /* Informations dynamiques */ -#endif - GPluginModule *self; /* Représentation interne */ - PluginStatusFlags self_flags; /* Fanions à mettre à jour */ - - static PyMethodDef py_chrysalide_methods[] = { - PY_CHRYSALIDE_REVISION_METHOD, - PY_CHRYSALIDE_VERSION_METHOD, - PY_CHRYSALIDE_MOD_VERSION_METHOD, - { NULL } - }; - - static PyModuleDef py_chrysalide_module = { - - .m_base = PyModuleDef_HEAD_INIT, - - .m_name = "pychrysalide", - .m_doc = PYCHRYSALIDE_DOC, - - .m_size = -1, + bool result; /* Bilan à retourner */ + GPyChrysalidePlugin *pychr_plugin; /* Version spécialisée */ + bool standalone; /* Nature du chargement */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - .m_methods = py_chrysalide_methods + result = true; - }; + pychr_plugin = G_PYCHRYSALIDE_PLUGIN(plugin); /** - * Vérification préalable : dans le cas où on est embarqué directement dans - * un interpréteur Python, le module se charge et termine par charger à leur - * tour les différentes extensions trouvées, via load_remaning_plugins() puis - * chrysalide_plugin_on_native_loaded(). - * - * Lesquelles vont très probablement charger le module pychrysalide. - * - * Comme le chargement de ce dernier n'est alors pas encore terminé, - * Python va relancer cette procédure, et register_access_to_python_module() - * va détecter un doublon. + * Le champ plugin->py_module n'est défini que via la fonction + * g_pychrysalide_plugin_enable(), qui n'est pas sollicitée lorsque + * le module PyChrysalide est mis en place directement par Python. + * + * L'analyse de ce champ pour retrouver la situation courante est + * plus fiable que celle du champ _standalone, potentiellement + * cohérent dans la version UI du greffon et resté à son état + * initial ici. */ - result = get_access_to_python_module(py_chrysalide_module.m_name); - - if (result != NULL) - { - Py_INCREF(result); - return result; - } - - if (!is_current_abi_suitable()) - goto exit; - - if (pygobject_init(-1, -1, -1) == NULL) - { - PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python."); - goto exit; - } - - if (!install_metaclass_for_python_gobjects()) - goto exit; + standalone = (pychr_plugin->py_module == NULL); /** - * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil, - * à savoir des types convertis de façon incomplète. Par exemple, pour une - * structure GChecksum, le type à l'exécution est : - * - * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>] - * - * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>] + * Si on se trouve embarqué dans un interpréteur Python, le déchargement + * des greffons est organisé à partir de la fonction PyExit_pychrysalide(), + * directement appelée depuis un contexte Python. * - * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec - * la version debug de Python3 (version de python3-gi : 3.42.2-3) : + * Un verrou n'est alors pas souhaité ici : * - * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module() + * python3d: ../Python/pystate.c:1687: PyGILState_Ensure: Assertion `gilstate->autoInterpreterState' failed. * - * Code de reproduction dans un interpréteur classique : + * Avec : * - * import gi - * gi.require_version('GLib', '2.0') - * from gi.repository import GLib + * $ python3d --version + * Python 3.11.2 * */ - if (!import_namespace_from_gi_repository("GLib", "2.0")) - goto exit; -#if 0 -#ifdef INCLUDE_GTK_SUPPORT - if (!set_version_for_gtk_namespace("3.0")) - goto exit; -#endif -#endif - - /* - if (!load_all_core_components(true)) - { - PyErr_SetString(PyExc_SystemError, "unable to load all basic components."); - goto exit; - } - */ - - /* Mise en place des fonctionnalités offertes */ - - result = PyModule_Create(&py_chrysalide_module); - - register_access_to_python_module(py_chrysalide_module.m_name, result); + if (!standalone) + gstate = PyGILState_Ensure(); - status = true; + clear_all_accesses_to_python_modules(); - if (status) status = add_features_module(result); + Py_XDECREF(pychr_plugin->py_module); + pychr_plugin->py_module = NULL; - if (status) status = add_analysis_module(result); - if (status) status = add_arch_module(result); - /* - if (status) status = add_common_module(result); - if (status) status = add_core_module(result); - if (status) status = add_debug_module(result); - if (status) status = add_format_module(result); - if (status) status = add_glibext_module(result); -#ifdef INCLUDE_GTK_SUPPORT - if (status) status = add_gtkext_module(result); - if (status) status = add_gui_module(result); -#endif - if (status) status = add_mangling_module(result); - */ - if (status) status = add_plugins_module(result); - - /* - if (status) status = ensure_python_string_enum_is_registered(); - if (status) status = ensure_python_py_struct_is_registered(); - */ - - if (status) status = define_data_types_constants(result); - - if (status) status = populate_analysis_module(); - if (status) status = populate_arch_module(); - /* - if (status) status = populate_common_module(); - if (status) status = populate_core_module(); - if (status) status = populate_debug_module(); - if (status) status = populate_format_module(); - if (status) status = populate_glibext_module(); -#ifdef INCLUDE_GTK_SUPPORT - if (status) status = populate_gtkext_module(); - if (status) status = populate_gui_module(); -#endif - if (status) status = populate_mangling_module(); - */ - if (status) status = populate_plugins_module(); + if (!standalone) + PyGILState_Release(gstate); - if (!status) - { - PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components."); - Py_DECREF(result); - result = NULL; - goto exit; - } - - if (_standalone) - { - ret = Py_AtExit(PyExit_pychrysalide); + return result; - if (ret == -1) - { - PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function."); - Py_DECREF(result); - result = NULL; - goto exit; - } +} - /** - * Comme les sources locales sont prioritaires, le fichier "core/global.h" - * du greffon masque la fonction suivante, issue du corps principal du - * programme. - * - * On la déclare donc à la main. - */ - /* - extern void set_batch_mode(void); - - set_batch_mode(); - */ - - /** - * Si cette extension pour Python est chargée depuis un dépôt Python, - * elle ne se trouve pas dans le répertoire classique des extensions et - * n'est donc pas chargée et enregistrée comme attendu. - * - * Cet enregistrement est donc forcé ici. - */ - -#ifdef PYTHON_PACKAGE - - ret = dladdr(__FUNCTION__, &info); - if (ret == 0) - { - LOG_ERROR_DL_N("dladdr"); - Py_DECREF(result); - result = NULL; - goto exit; - } - self = g_plugin_module_new(info.dli_fname); - assert(self != NULL); - register_plugin(self); +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ -#endif - init_all_plugins(false); +/****************************************************************************** +* * +* Paramètres : modname = nom du module à charger. * +* filename = chemin d'accès au code Python à charger. * +* * +* Description : Crée un greffon à partir de code Python. * +* * +* Retour : Adresse de la structure mise en place ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ - lock_plugin_list_for_reading(); +static GPluginModule *create_python_plugin(const char *modname, const char *filename) +{ + GPluginModule *result; /* Structure à retourner */ + PyObject *name; /* Chemin d'accès pour Python */ + PyObject *module; /* Script Python chargé */ + PyObject *dict; /* Dictionnaire associé */ + PyObject *class; /* Classe à instancier */ + PyObject *instance; /* Instance Python du greffon */ - self = get_plugin_by_name("PyChrysalide", NULL); - assert(self != NULL); + name = PyUnicode_FromString(modname); + if (name == NULL) goto bad_exit; - self_flags = g_plugin_module_get_flags(self); - self_flags &= ~(PSF_FAILURE | PSF_LOADED); - self_flags |= (status ? PSF_LOADED : PSF_FAILURE); + module = PyImport_Import(name); + Py_DECREF(name); - g_plugin_module_override_flags(self, self_flags); + if (module == NULL) goto no_import; - unlock_plugin_list_for_reading(); + dict = PyModule_GetDict(module); + class = PyDict_GetItemString(dict, "AutoLoad"); - load_remaning_plugins(); + if (class == NULL) goto no_class; + if (!PyType_Check(class->ob_type)) goto no_class; - /** - * On laisse fuir ici la référence sur self afin d'avoir - * l'assurance que le greffon se déchargera toujours en dernier. - * - * La fuite mémoire est au final évitée dans PyExit_pychrysalide(). - */ + instance = PyObject_CallFunction(class, NULL); + if (instance == NULL) goto no_instance; - } + result = G_PLUGIN_MODULE(pygobject_get(instance)); - exit: + /** + * L'instance Python et l'objet GLib résultant sont un même PyGObject. + * + * Donc pas besoin de toucher au comptage des références ici, la libération + * se réalisera à la fin, quand l'objet GLib sera libéré. + */ - if (result == NULL && !_standalone) - log_pychrysalide_exception("Loading failed"); + Py_DECREF(module); return result; -} + no_instance: -/****************************************************************************** -* * -* Paramètres : path = chemin supplémentaire pour l'espace de recherche. * -* * -* Description : Complète les chemins de recherches de Python. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance")); -static void extend_python_path(const char *path) -{ - PyObject *list; /* Liste de chemins à compléter*/ - PyObject *new; /* Nouveau chemin à intégrer */ + no_class: - list = PySys_GetObject("path"); - assert(list != NULL); + if (class == NULL) + log_plugin_simple_message(LMT_ERROR, + _("An error occured when looking for the 'AutoLoad': item not found!")); - new = PyUnicode_FromString(path); - assert(new != NULL); + no_import: - PyList_Append(list, new); + Py_XDECREF(module); - Py_DECREF(new); + log_pychrysalide_exception(_("An error occured when importing '%s'"), modname); - add_to_env_var("PYTHONPATH", path, ":"); + bad_exit: + + return NULL; } @@ -822,49 +519,18 @@ static void extend_python_path(const char *path) static void load_python_plugins(GPluginModule *plugin) { -#ifdef DISCARD_LOCAL - char *edir; /* Répertoire de base effectif */ -#endif - DIR *dir; /* Répertoire à parcourir */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ char *paths; /* Emplacements de greffons */ char *save; /* Sauvegarde pour ré-entrance */ char *path; /* Chemin à fouiller */ + DIR *dir; /* Répertoire à parcourir */ struct dirent *entry; /* Elément trouvé */ char *modname; /* Nom du module pour Python */ char *filename; /* Chemin d'accès reconstruit */ GPluginModule *pyplugin; /* Lien vers un grffon Python */ bool status; /* Bilan d'une opération */ - //GGenConfig *config; /* Configuration à charger */ - - /* Définition des zones d'influence */ - -#ifndef DISCARD_LOCAL - - extend_python_path(PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" G_DIR_SEPARATOR_S "python"); -#else - - edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python"); - dir = opendir(edir); - free(edir); - - if (dir != NULL) - { - closedir(dir); - - edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python"); - extend_python_path(edir); - free(edir); - - } - -#endif - - g_plugin_module_log_variadic_message(plugin, LMT_INFO, - _("PYTHONPATH environment variable set to '%s'"), - getenv("PYTHONPATH")); - - /* Chargements des extensions Python */ + gstate = PyGILState_Ensure(); paths = get_env_var("PYTHONPATH"); @@ -877,7 +543,7 @@ static void load_python_plugins(GPluginModule *plugin) dir = opendir(path); if (dir == NULL) { - perror("opendir"); + LOG_ERROR_N("opendir"); continue; } @@ -894,7 +560,7 @@ static void load_python_plugins(GPluginModule *plugin) if (entry == NULL) { if (errno != 0) - perror("readdir"); + LOG_ERROR_N("readdir"); break; @@ -921,8 +587,7 @@ static void load_python_plugins(GPluginModule *plugin) goto done_with_plugin; } - //g_plugin_module_create_config(pyplugin); - + /* status = g_plugin_module_manage(pyplugin, PGA_PLUGIN_LOADED); if (!status) @@ -932,18 +597,19 @@ static void load_python_plugins(GPluginModule *plugin) g_object_unref(G_OBJECT(pyplugin)); goto done_with_plugin; } - - /* - config = g_plugin_module_get_config(pyplugin); - g_generic_config_read(config); - g_object_unref(G_OBJECT(config)); */ g_plugin_module_log_variadic_message(plugin, LMT_PROCESS, _("Loaded the Python plugin found in the '<b>%s</b>' directory"), filename); - _register_plugin(pyplugin); + printf(" -> BUG // %p\n", pyplugin); + + printf(" -> BUG // %u\n", ((GObject *)pyplugin)->ref_count); + + //register_plugin(pyplugin); + + /////////unref_object(pyplugin); done_with_plugin: @@ -958,90 +624,6 @@ static void load_python_plugins(GPluginModule *plugin) free(paths); -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* * -* Description : Prend acte du chargement du greffon. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) -{ - bool result; /* Bilan à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - int ret; /* Bilan de préparatifs */ - - _standalone = false; - - /* Chargement du module pour Python */ - - ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide); - - if (ret == -1) - { - log_plugin_simple_message(LMT_ERROR, _("Can not extend the existing table of Python built-in modules.")); - result = false; - goto cpi_done; - } - - Py_Initialize(); - - gstate = PyGILState_Ensure(); - - _chrysalide_module = PyImport_ImportModule("pychrysalide"); - - /** - * Pour mémoire, une situation concrête conduisant à un échec : - * le paquet python3-gi-dbg n'est pas installé alors que le - * programme est compilé en mode débogage. - * - * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() - * le laisse rien filtrer... - * - * En mode autonome, le shell Python remonte bien l'erreur par contre. - */ - - result = (_chrysalide_module != NULL); - - PyGILState_Release(gstate); - - cpi_done: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* * -* Description : Prend acte du déchargement du greffon. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - - gstate = PyGILState_Ensure(); - - clear_all_accesses_to_python_modules(); - - Py_XDECREF(_chrysalide_module); - PyGILState_Release(gstate); } @@ -1049,30 +631,7 @@ G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : type = informations à libérer de la mémoire. * -* * -* Description : Efface un type Python pour greffon de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void free_native_plugin_type(PyTypeObject *type) -{ - free((char *)type->tp_name); - free((char *)type->tp_doc); - - free(type); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * +* Paramètres : manager = interface à manipuler. * * * * Description : Accompagne la fin du chargement des modules natifs. * * * @@ -1082,308 +641,78 @@ static void free_native_plugin_type(PyTypeObject *type) * * ******************************************************************************/ -G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, PluginAction action) +static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPluginManager *manager) { - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - size_t count; /* Quantité de greffons chargés*/ - PyObject *module; /* Module à recompléter */ - PyObject *dict; /* Dictionnaire du module */ - GPluginModule **list; /* Ensemble de ces greffons */ - size_t i; /* Boucle de parcours */ - char *name; /* Désignation complète */ - char *doc; /* Description adaptée */ - int ret; /* Bilan d'un appel */ - PyTypeObject *type; /* Nouveau type dynamique */ - - gstate = PyGILState_Ensure(); - - if (action == PGA_NATIVE_PLUGINS_LOADED) - { - /* Intégration des greffons natifs en Python */ - - if (ensure_python_plugin_module_is_registered()) - { - module = get_access_to_python_module("pychrysalide.plugins"); - assert(module != NULL); - - dict = PyModule_GetDict(module); - - list = get_all_plugins(&count); - - for (i = 0; i < count; i++) - { - ret = asprintf(&name, "pychrysalide.plugins.%s", G_OBJECT_TYPE_NAME(list[i]) + 1); - if (ret == -1) - { - LOG_ERROR_N("asprintf"); - continue; - } - - ret = asprintf(&doc, "Place holder for the native plugin %s documentation", - G_OBJECT_TYPE_NAME(list[i]) + 1); - if (ret == -1) - { - LOG_ERROR_N("asprintf"); - free(name); - continue; - } - - type = calloc(1, sizeof(PyTypeObject)); - - type->tp_name = name; - type->tp_doc = doc; - type->tp_flags = Py_TPFLAGS_DEFAULT; - type->tp_new = no_python_constructor_allowed; - - if (register_class_for_pygobject(dict, G_OBJECT_TYPE(list[i]), type)) - g_object_set_data_full(G_OBJECT(list[i]), "python_type", type, - (GDestroyNotify)free_native_plugin_type); - - else - free_native_plugin_type(type); - - } - - if (list != NULL) - free(list); - - } - - /* Chargement des extensions purement Python */ - - load_python_plugins(plugin); - - } - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* type = type d'objet à mettre en place. * -* * -* Description : Crée une instance à partir d'un type dynamique externe. * -* * -* Retour : Instance d'objet gérée par l'extension ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type) -{ - gpointer result; /* Instance à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyTypeObject *pytype; /* Classe Python concernée */ - PyObject *instance; /* Initialisation forcée */ - - result = NULL; - - gstate = PyGILState_Ensure(); - - pytype = pygobject_lookup_class(type); - - if (pytype != NULL) - { - instance = PyObject_CallObject((PyObject *)pytype, NULL); - assert(instance != NULL); - - result = pygobject_get(instance); - - } - - PyGILState_Release(gstate); - - return result; + GPluginModule *plugin; /* Version de base du greffon */ +#ifdef DISCARD_LOCAL + char *edir; /* Répertoire de base effectif */ + DIR *dir; /* Répertoire à parcourir */ +#endif -} + plugin = G_PLUGIN_MODULE(manager); + /* Définition des zones d'influence */ -/****************************************************************************** -* * -* Paramètres : namespace = module particulier à charger à partir de gi. * -* version = idenfiant de la version à stipuler. * -* * -* Description : Charge un module GI dans Python avec une version attendue. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ +#ifndef DISCARD_LOCAL -bool import_namespace_from_gi_repository(const char *namespace, const char *version) -{ - bool result; /* Bilan à retourner */ - PyObject *module; /* Module Python-GObject */ - PyObject *args; /* Arguments à fournir */ - int ret; /* Bilan d'une mise en place */ + extend_python_path(plugin, PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" G_DIR_SEPARATOR_S "python"); - result = false; +#else - /* Sélection d'une version */ + edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python"); - module = PyImport_ImportModule("gi"); + dir = opendir(edir); - if (module != NULL) + if (dir != NULL) { - args = Py_BuildValue("ss", namespace, version); - - run_python_method(module, "require_version", args); - - result = (PyErr_Occurred() == NULL); + closedir(dir); - Py_DECREF(args); - Py_DECREF(module); + extend_python_path(plugin, edir); } - /* Importation du module visé */ - - if (result) - { - args = PyTuple_New(1); + free(edir); - ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace)); - if (ret != 0) - { - result = false; - goto args_error; - } +#endif - module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args); + /* Chargements des extensions Python */ - result = (module != NULL); + load_python_plugins(plugin); - Py_XDECREF(module); +} - args_error: - Py_DECREF(args); - } - - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* POINT D'ENTREE POUR PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.* +* Paramètres : - * * * -* Description : Présente dans le journal une exception survenue. * +* Description : Point d'entrée pour l'initialisation de Python. * * * -* Retour : - * +* Retour : ? * * * * Remarques : - * * * ******************************************************************************/ -void log_pychrysalide_exception(const char *prefix, ...) +PyMODINIT_FUNC PyInit_pychrysalide(void) { - va_list ap; /* Compléments argumentaires */ - char *msg; /* Message complet à imprimer */ - PyObject *err_type; /* Type d'erreur Python */ - PyObject *err_value; /* Instance Python d'erreur */ - PyObject *err_traceback; /* Trace Python associée */ - PyObject *err_string; /* Description Python d'erreur */ - const char *err_msg; /* Représentation humaine */ - - assert(PyGILState_Check() == 1); - - if (PyErr_Occurred()) - { - /* Base de la communication */ - - va_start(ap, prefix); - - vasprintf(&msg, prefix, ap); - - va_end(ap); - - /* Détails complémentaires */ - - PyErr_Fetch(&err_type, &err_value, &err_traceback); - - PyErr_NormalizeException(&err_type, &err_value, &err_traceback); - - if (err_traceback == NULL) - { - err_traceback = Py_None; - Py_INCREF(err_traceback); - } - - PyException_SetTraceback(err_value, err_traceback); - - if (err_value == NULL) - msg = stradd(msg, _(": no extra information is provided...")); - - else - { - err_string = PyObject_Str(err_value); - err_msg = PyUnicode_AsUTF8(err_string); + PyObject *result; /* Module Python à retourner */ + pyinit_details_t details; /* Détails de chargement */ - msg = stradd(msg, ": "); - msg = stradd(msg, err_msg); + details.standalone = _standalone; - Py_DECREF(err_string); + details.add_extra = NULL; + details.populate_extra = NULL; + details.create_self = g_pychrysalide_plugin_new; - } + result = init_python_pychrysalide_module(&details); - /** - * Bien que la documentation précise que la fonction PyErr_Fetch() - * transfère la propritété des éléments retournés, la pratique - * montre que le programme plante à la terminaison en cas d'exception. - * - * C'est par exemple le cas quand un greffon Python ne peut se lancer - * correctement ; l'exception est alors levée à partir de la fonction - * create_python_plugin() et le plantage intervient en sortie d'exécution, - * au moment de la libération de l'extension Python : - * - * ==14939== Jump to the invalid address stated on the next line - * ==14939== at 0x1A8FCBC9: ??? - * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3) - * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370) - * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153) - * ==14939== by 0x10AD19: main (main.c:440) - * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd - * - * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un - * appel à PyErr_PrintEx(0) ne change rien. - * - * La seule différence de l'instruction set_sys_last_vars réside en quelques - * lignes dans le code de l'interpréteur Python : - * - * if (set_sys_last_vars) { - * _PySys_SetObjectId(&PyId_last_type, exception); - * _PySys_SetObjectId(&PyId_last_value, v); - * _PySys_SetObjectId(&PyId_last_traceback, tb); - * } - * - * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ? - * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence - * à ces éléments. - * - * On reproduit ici le comportement du code correcteur avec PySys_SetObject(). - */ - - PySys_SetObject("last_type", err_type); - PySys_SetObject("last_value", err_value); - PySys_SetObject("last_traceback", err_traceback); - - Py_XDECREF(err_traceback); - Py_XDECREF(err_value); - Py_XDECREF(err_type); - - log_plugin_simple_message(LMT_ERROR, msg); - - free(msg); - - } + return result; } diff --git a/plugins/pychrysalide/core.h b/plugins/pychrysalide/core.h index 5d25d3d..60c6c93 100644 --- a/plugins/pychrysalide/core.h +++ b/plugins/pychrysalide/core.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.h - prototypes pour le plugin permettant des extensions en Python * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -35,31 +35,29 @@ #include <Python.h> +#include <glibext/helpers.h> #include <plugins/plugin.h> -#include <plugins/plugin-int.h> -/* Point d'entrée pour l'initialisation de Python. */ -PyMODINIT_FUNC PyInit_pychrysalide(void); +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +#define G_TYPE_PYCHRYSALIDE_PLUGIN (g_pychrysalide_plugin_get_type()) -/* Prend acte du chargement du greffon. */ -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *); +DECLARE_GTYPE(GPyChrysalidePlugin, g_pychrysalide_plugin, G, PYCHRYSALIDE_PLUGIN); -/* Prend acte du déchargement du greffon. */ -G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *); -/* Accompagne la fin du chargement des modules natifs. */ -G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *, PluginAction); +/* Crée un module pour un greffon de support Python. */ +GPluginModule *g_pychrysalide_plugin_new(GModule *); -/* Crée une instance à partir d'un type dynamique externe. */ -G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *, PluginAction, GType); -/* Charge un module GI dans Python avec une version attendue. */ -bool import_namespace_from_gi_repository(const char *, const char *); -/* Présente dans le journal une exception survenue. */ -void log_pychrysalide_exception(const char *, ...); +/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */ + + +/* Point d'entrée pour l'initialisation de Python. */ +PyMODINIT_FUNC PyInit_pychrysalide(void); diff --git a/plugins/pychrysalide/core/Makefile.am b/plugins/pychrysalide/core/Makefile.am index a50324a..5588c9f 100644 --- a/plugins/pychrysalide/core/Makefile.am +++ b/plugins/pychrysalide/core/Makefile.am @@ -14,7 +14,8 @@ noinst_LTLIBRARIES = libpychrysacore.la libpychrysacore_la_SOURCES = \ constants.h constants.c \ logs.h logs.c \ - module.h module.c + module.h module.c \ + nox.h nox.c libpychrysacore_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/core/module.c b/plugins/pychrysalide/core/module.c index b588057..4af0403 100644 --- a/plugins/pychrysalide/core/module.c +++ b/plugins/pychrysalide/core/module.c @@ -31,6 +31,7 @@ //#include "demanglers.h" //#include "global.h" #include "logs.h" +#include "nox.h" //#include "params.h" //#include "processors.h" //#include "queue.h" @@ -105,6 +106,7 @@ bool populate_core_module(void) //if (result) result = populate_core_module_with_demanglers(); //if (result) result = populate_core_module_with_global(); if (result) result = populate_core_module_with_logs(); + if (result) result = populate_core_module_with_nox(); //if (result) result = populate_core_module_with_params(); //if (result) result = populate_core_module_with_processors(); //if (result) result = populate_core_module_with_queue(); diff --git a/plugins/pychrysalide/core/nox.c b/plugins/pychrysalide/core/nox.c new file mode 100644 index 0000000..46a4847 --- /dev/null +++ b/plugins/pychrysalide/core/nox.c @@ -0,0 +1,110 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * nox.c - équivalent Python du fichier "core/nox.c" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "nox.h" + + +#include <pygobject.h> + + +#include <core/nox.h> +#include <plugins/self.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* Indique la présence ou l'absence d'un affichage graphique. */ +static PyObject *py_nox_run_in_nox_mode(PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Indique la présence ou l'absence d'un affichage graphique. * +* * +* Retour : Statut à transmettre. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_nox_run_in_nox_mode(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + bool status; /* Statut à retransmettre */ + +#define NOX_RUN_IN_NOX_MODE_METHOD PYTHON_METHOD_DEF \ +( \ + run_in_nox_mode, "", \ + METH_NOARGS, py_nox, \ + "Tell if Chrysalide runs with a graphical support or not." \ + "\n" \ + "The result is a boolean status: *True* in case of terminal only mode." \ +) + + status = run_in_nox_mode(); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Définit une extension du module 'core' à compléter. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_core_module_with_nox(void) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Module à recompléter */ + + static PyMethodDef py_nox_methods[] = { + NOX_RUN_IN_NOX_MODE_METHOD, + { NULL } + }; + + module = get_access_to_python_module("pychrysalide.core"); + + result = register_python_module_methods(module, py_nox_methods); + + return result; + +} diff --git a/plugins/pychrysalide/core/nox.h b/plugins/pychrysalide/core/nox.h new file mode 100644 index 0000000..042f5a8 --- /dev/null +++ b/plugins/pychrysalide/core/nox.h @@ -0,0 +1,39 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * nox.h - prototypes pour l'équivalent Python du fichier "core/nox.h" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_CORE_NOX_H +#define _PLUGINS_PYCHRYSALIDE_CORE_NOX_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Définit une extension du module 'core' à compléter. */ +bool populate_core_module_with_nox(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_NOX_H */ diff --git a/plugins/pychrysalide/format/Makefile.am b/plugins/pychrysalide/format/Makefile.am index 337265d..efe58f3 100644 --- a/plugins/pychrysalide/format/Makefile.am +++ b/plugins/pychrysalide/format/Makefile.am @@ -1,19 +1,26 @@ noinst_LTLIBRARIES = libpychrysaformat.la +# libpychrysaformat_la_SOURCES = \ +# constants.h constants.c \ +# executable.h executable.c \ +# flat.h flat.c \ +# program.h program.c \ +# known.h known.c \ +# module.h module.c \ +# preload.h preload.c \ +# strsym.h strsym.c \ +# symbol.h symbol.c \ +# symiter.h symiter.c + libpychrysaformat_la_SOURCES = \ - constants.h constants.c \ executable.h executable.c \ flat.h flat.c \ - format.h format.c \ known.h known.c \ module.h module.c \ - preload.h preload.c \ - strsym.h strsym.c \ - symbol.h symbol.c \ - symiter.h symiter.c + program.h program.c -libpychrysaformat_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpychrysaformat_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/format/executable.c b/plugins/pychrysalide/format/executable.c index ff2d14a..f0d3d6b 100644 --- a/plugins/pychrysalide/format/executable.c +++ b/plugins/pychrysalide/format/executable.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * executable.c - équivalent Python du fichier "format/executable.c" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -30,29 +30,369 @@ #include <i18n.h> -#include <core/processors.h> +#include <format/executable-int.h> -#include "format.h" +#include "program.h" #include "../access.h" #include "../helpers.h" -#include "../arch/processor.h" +#include "../analysis/content.h" +//#include "../arch/processor.h" #include "../arch/vmpa.h" -#include "../glibext/binportion.h" +#include "../glibext/portion.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Initialise la classe des formats exécutables. */ +static int py_executable_format_init_gclass(GExecutableFormatClass *, PyTypeObject *); + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_executable_format_init(PyObject *, PyObject *, PyObject *); + +/* Indique le type d'architecture visée par le format. */ +static char *py_executable_format_get_target_machine_wrapper(const GExecutableFormat *); + +/* Fournit l'adresse principale associée à un format. */ +static bool py_executable_format_get_main_address_wrapper(GExecutableFormat *, vmpa2t *); + +/* Etend la définition des portions au sein d'un binaire. */ +static bool py_executable_format_refine_portions_wrapper(GExecutableFormat *); /* ------------------------ DECLARATION DE FORMAT EXECUTABLE ------------------------ */ -/* Enregistre une portion artificielle pour le format. */ -static PyObject *py_exe_format_register_user_portion(PyObject *, PyObject *); +/* Procède à l'enregistrement d'une portion dans un format. */ +static PyObject *py_executable_format_include_portion(PyObject *, PyObject *); /* Fournit l'emplacement correspondant à une position physique. */ -static PyObject *py_exe_format_translate_offset_into_vmpa(PyObject *, PyObject *); +static PyObject *py_executable_format_translate_offset_into_vmpa(PyObject *, PyObject *); /* Fournit l'emplacement correspondant à une adresse virtuelle. */ -static PyObject *py_exe_format_translate_address_into_vmpa(PyObject *, PyObject *); +static PyObject *py_executable_format_translate_address_into_vmpa(PyObject *, PyObject *); + +/* Indique le type d'architecture visée par le format. */ +static PyObject *py_executable_format_get_target_machine(PyObject *, void *); + +/* Fournit l'adresse principale associée à un format. */ +static PyObject *py_executable_format_get_main_address(PyObject *, void *); + +/* Indique le type d'architecture visée par le format. */ +static PyObject *py_executable_format_get_portions(PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * +* * +* Description : Initialise la classe des formats exécutables. * +* * +* Retour : 0 pour indiquer un succès de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_executable_format_init_gclass(GExecutableFormatClass *gclass, PyTypeObject *pyclass) +{ + PY_CLASS_SET_WRAPPER(gclass->get_machine, py_executable_format_get_target_machine_wrapper); + + PY_CLASS_SET_WRAPPER(gclass->get_main_addr, py_executable_format_get_main_address_wrapper); + PY_CLASS_SET_WRAPPER(gclass->refine_portions, py_executable_format_refine_portions_wrapper); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_executable_format_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GBinContent *content; /* Contenu à intégrer au format*/ + int ret; /* Bilan de lecture des args. */ + GExecutableFormat *format; /* Format à manipuler */ + +#define EXECUTABLE_FORMAT_DOC \ + "The ExecutableFormat class provides support for formats containing"\ + " code to run.\n" \ + "\n" \ + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.format.ExecutableFormat._get_target_machine();\n" \ + "* pychrysalide.format.ExecutableFormat._get_main_address().\n" \ + "\n" \ + "The following method may be defined for new classes:\n" \ + "* pychrysalide.format.ExecutableFormat._refine_portions().\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect"\ + " only one argument: a binary content, provided as a" \ + " pychrysalide.analysis.BinContent instance." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&", convert_to_binary_content, &content); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + format = G_EXECUTABLE_FORMAT(pygobject_get(self)); + + if (!g_executable_format_create(format, content)) + return -1; + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : format = description du format exécutable à consulter. * +* * +* Description : Indique le type d'architecture visée par le format. * +* * +* Retour : Identifiant de l'architecture ciblée par le format. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *py_executable_format_get_target_machine_wrapper(const GExecutableFormat *format) +{ + char *result; /* Désignation à renvoyer */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Valeur retournée */ + int ret; /* Bilan d'une conversion */ + +#define EXECUTABLE_FORMAT_GET_TARGET_MACHINE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_target_machine, "$self", \ + METH_NOARGS, \ + "Abstract method used to define the identifier of the architecture" \ + " suitable for the executable format.\n" \ + "\n" \ + "The return value has to be a (tiny) string." \ +) + + result = NULL; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(format)); + + if (has_python_method(pyobj, "_get_target_machine")) + { + pyret = run_python_method(pyobj, "_get_target_machine", NULL); + + if (pyret != NULL) + { + ret = PyUnicode_Check(pyret); + + if (ret) + result = strdup(PyUnicode_AsUTF8(pyret)); + else + PyErr_SetString(PyExc_ValueError, _("unexpected value type for executable format target machine")); + + Py_DECREF(pyret); + + } + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : format = description de l'exécutable à consulter. * +* addr = adresse principale trouvée si possible. [OUT] * +* * +* Description : Fournit l'adresse principale associée à un format. * +* * +* Retour : Validité de l'adresse transmise. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_executable_format_get_main_address_wrapper(GExecutableFormat *format, vmpa2t *addr) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Valeur retournée */ + vmpa2t *tmp; /* Zone de stockage Python */ + int ret; /* Bilan d'une conversion */ + +#define EXECUTABLE_FORMAT_GET_MAIN_ADDRESS_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_main_address, "$self", \ + METH_NOARGS, \ + "Abstract method used to provide the main address of code for" \ + " the executable format.\n" \ + "\n" \ + "The return value has to be a pychrysalide.arch.vmpa instance or" \ + " *None* in case of failure." \ + ) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(format)); + + if (has_python_method(pyobj, "_get_main_address")) + { + pyret = run_python_method(pyobj, "_get_main_address", NULL); + + if (pyret != NULL) + { + if (pyret == Py_None) + { + init_vmpa(addr, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + result = true; + } + + else + { + ret = convert_any_to_vmpa(pyret, &tmp); + + result = (ret == 1 || ret == Py_CLEANUP_SUPPORTED); + + if (result) + { + copy_vmpa(addr, tmp); + + if (ret == Py_CLEANUP_SUPPORTED) + clean_vmpa_arg(tmp); + + } + + else + { + /** + * L'erreur Python peut être effacée. + * + * Elle sera remontée : + * - au code C via le retour (false) : + * - à Python lors de l'accès à la propriétée. + */ + PyErr_Clear(); + + } + + } + + Py_DECREF(pyret); + + } + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : format = informations chargées à consulter. * +* * +* Description : Etend la définition des portions au sein d'un binaire. * +* * +* Retour : Bilan des définitions de portions. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_executable_format_refine_portions_wrapper(GExecutableFormat *format) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Valeur retournée */ + +#define EXECUTABLE_FORMAT_REFINE_PORTIONS_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _refine_portions, "$self", \ + METH_NOARGS, \ + "Abstract method used to extend the definition of the format" \ + " with binary portions.\n" \ + "\n" \ + "Extra portions should be included with calls to" \ + " pychrysalide.format.ExecutableFormat.include_portion().\n" \ + "\n" \ + "The return value has to be a boolean value: *True* in case of" \ + " success, *False* in case of failure." \ +) + + result = true; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(format)); + + if (has_python_method(pyobj, "_refine_portions")) + { + pyret = run_python_method(pyobj, "_refine_portions", NULL); + + if (pyret != NULL) + { + result = (pyret == Py_True); + Py_DECREF(pyret); + } + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} @@ -66,29 +406,54 @@ static PyObject *py_exe_format_translate_address_into_vmpa(PyObject *, PyObject * Paramètres : self = description de l'exécutable à consulter. * * args = arguments accompagnant l'appel. * * * -* Description : Enregistre une portion artificielle pour le format. * +* Description : Procède à l'enregistrement d'une portion dans un format. * * * -* Retour : - * +* Retour : Bilan de l'opération : True si inclusion, False sinon. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_exe_format_register_user_portion(PyObject *self, PyObject *args) +static PyObject *py_executable_format_include_portion(PyObject *self, PyObject *args) { - GBinPortion *portion; /* Portion binaire à conserver */ + PyObject *result; /* Bilan à retourner */ + GBinaryPortion *portion; /* Portion binaire à conserver */ + vmpa2t *origin; /* Source de l'inclusion */ int ret; /* Bilan de lecture des args. */ - GExeFormat *format; /* Version GLib du format */ - - ret = PyArg_ParseTuple(args, "O&", convert_to_binary_portion, &portion); + GExecutableFormat *format; /* Version GLib du format */ + bool status; /* Bilan de l'inclusion */ + +#define EXECUTABLE_FORMAT_INCLUDE_PORTION_METHOD PYTHON_METHOD_DEF \ +( \ + include_portion, "$self, portion, /, origin=None", \ + METH_VARARGS, py_executable_format, \ + "Register a new portion inside the content of an executable format.\n" \ + "\n" \ + "The *portion* argument is a pychrysalide.glibext.BinaryPortion" \ + " instance. The optional *origin* arguement specifies the source of the"\ + " operation, as a pychrysalide.arch.vmpa definition, which may be used" \ + " for tracking errors.\n" \ + "\n" \ + "The return value is a boolean value: *True* in case of success," \ + " *False* in case of failure." \ +) + + origin = NULL; + + ret = PyArg_ParseTuple(args, "O&|O&", convert_to_binary_portion, &portion, convert_any_to_vmpa, &origin); if (!ret) return NULL; - format = G_EXE_FORMAT(pygobject_get(self)); + format = G_EXECUTABLE_FORMAT(pygobject_get(self)); + + status = g_executable_format_include_portion(format, portion, origin); - g_object_ref(G_OBJECT(portion)); - g_exe_format_register_user_portion(format, portion); + result = status ? Py_True : Py_False; + Py_INCREF(result); - Py_RETURN_NONE; + if (origin != NULL) + clean_vmpa_arg(origin); + + return result; } @@ -106,22 +471,33 @@ static PyObject *py_exe_format_register_user_portion(PyObject *self, PyObject *a * * ******************************************************************************/ -static PyObject *py_exe_format_translate_offset_into_vmpa(PyObject *self, PyObject *args) +static PyObject *py_executable_format_translate_offset_into_vmpa(PyObject *self, PyObject *args) { PyObject *result; /* Instance à retourner */ - GExeFormat *format; /* Version GLib du format */ + GExecutableFormat *format; /* Version GLib du format */ unsigned long long off; /* Adresse en mémoire virtuelle*/ int ret; /* Bilan de lecture des args. */ vmpa2t pos; /* Position complète déterminée*/ bool status; /* Bilan de l'opération */ - format = G_EXE_FORMAT(pygobject_get(self)); - assert(format != NULL); +#define EXECUTABLE_FORMAT_TRANSLATE_OFFSET_INTO_VMPA_METHOD PYTHON_METHOD_DEF \ +( \ + translate_offset_into_vmpa, "$self, addr", \ + METH_VARARGS, py_executable_format, \ + "Translate a physical offset to a full location.\n" \ + "\n" \ + "The *off* argument is a physical offset provided as an integer value.\n" \ + "\n" \ + "The returned position is a pychrysalide.arch.vmpa instance or *None* in" \ + " case of failure." \ +) ret = PyArg_ParseTuple(args, "K", &off); if (!ret) return NULL; - status = g_exe_format_translate_offset_into_vmpa(format, off, &pos); + format = G_EXECUTABLE_FORMAT(pygobject_get(self)); + + status = g_executable_format_translate_offset_into_vmpa(format, off, &pos); if (status) result = build_from_internal_vmpa(&pos); @@ -150,22 +526,33 @@ static PyObject *py_exe_format_translate_offset_into_vmpa(PyObject *self, PyObje * * ******************************************************************************/ -static PyObject *py_exe_format_translate_address_into_vmpa(PyObject *self, PyObject *args) +static PyObject *py_executable_format_translate_address_into_vmpa(PyObject *self, PyObject *args) { PyObject *result; /* Instance à retourner */ - GExeFormat *format; /* Version GLib du format */ + GExecutableFormat *format; /* Version GLib du format */ unsigned long long addr; /* Adresse en mémoire virtuelle*/ int ret; /* Bilan de lecture des args. */ vmpa2t pos; /* Position complète déterminée*/ bool status; /* Bilan de l'opération */ - format = G_EXE_FORMAT(pygobject_get(self)); - assert(format != NULL); +#define EXECUTABLE_FORMAT_TRANSLATE_ADDRESS_INTO_VMPA_METHOD PYTHON_METHOD_DEF \ +( \ + translate_address_into_vmpa, "$self, addr", \ + METH_VARARGS, py_executable_format, \ + "Translate a virtual address to a full location.\n" \ + "\n" \ + "The *addr* argument is a virtual address provided as an integer value.\n" \ + "\n" \ + "The returned position is a pychrysalide.arch.vmpa instance or *None* in" \ + " case of failure." \ +) ret = PyArg_ParseTuple(args, "K", &addr); if (!ret) return NULL; - status = g_exe_format_translate_address_into_vmpa(format, addr, &pos); + format = G_EXECUTABLE_FORMAT(pygobject_get(self)); + + status = g_executable_format_translate_address_into_vmpa(format, addr, &pos); if (status) result = build_from_internal_vmpa(&pos); @@ -183,6 +570,140 @@ static PyObject *py_exe_format_translate_address_into_vmpa(PyObject *self, PyObj /****************************************************************************** * * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le type d'architecture visée par le format. * +* * +* Retour : Identifiant de l'architecture ciblée par le format. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_executable_format_get_target_machine(PyObject *self, void *closure) +{ + PyObject *result; /* Trouvailles à retourner */ + GExecutableFormat *format; /* Format exécutable manipulé */ + char *machine; /* Désignation machine ciblée */ + +#define EXECUTABLE_FORMAT_TARGET_MACHINE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + target_machine, py_executable_format, \ + "Identifier of the architecture suitable for the executable format,"\ + " provided as a (tiny) string." \ +) + + format = G_EXECUTABLE_FORMAT(pygobject_get(self)); + + machine = g_executable_format_get_target_machine(format); + + if (machine != NULL) + { + result = PyUnicode_FromString(machine); + free(machine); + } + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_ValueError, _("unexpected NULL value for executable format target machine")); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit l'adresse principale associée à un format. * +* * +* Retour : Validité de l'adresse transmise. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_executable_format_get_main_address(PyObject *self, void *closure) +{ + PyObject *result; /* Trouvailles à retourner */ + GExecutableFormat *format; /* Format exécutable manipulé */ + vmpa2t addr; /* Point d'entrée principal */ + bool status; /* Validité de l'adresse */ + +#define EXECUTABLE_FORMAT_MAIN_ADDRESS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + main_address, py_executable_format, \ + "Main address of code for the executable format.\n" \ + "\n" \ + "This property provide a pychrysalide.arch.vmpa instance or" \ + " *None* in case of failure." \ +) + + format = G_EXECUTABLE_FORMAT(pygobject_get(self)); + + status = g_executable_format_get_main_address(format, &addr); + + if (status) + result = build_from_internal_vmpa(&addr); + + else + { + PyErr_SetString(PyExc_AttributeError, _("unable to define a value for the main address")); + result = NULL; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le type d'architecture visée par le format. * +* * +* Retour : Identifiant de l'architecture ciblée par le format. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_executable_format_get_portions(PyObject *self, void *closure) +{ + PyObject *result; /* Trouvailles à retourner */ + GExecutableFormat *format; /* Format exécutable manipulé */ + GBinaryPortion *portions; /* Portion principale du format*/ + +#define EXECUTABLE_FORMAT_PORTIONS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + portions, py_executable_format, \ + "Root portion of the executable format, provided as a" \ + " pychrysalide.glibext.BinaryPortion instance." \ +) + + format = G_EXECUTABLE_FORMAT(pygobject_get(self)); + + portions = g_executable_format_get_portions(format); + + result = pygobject_new(G_OBJECT(portions)); + unref_object(portions); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : - * * * * Description : Fournit un accès à une définition de type à diffuser. * @@ -195,46 +716,43 @@ static PyObject *py_exe_format_translate_address_into_vmpa(PyObject *self, PyObj PyTypeObject *get_python_executable_format_type(void) { - static PyMethodDef py_exe_format_methods[] = { - { - "register_user_portion", py_exe_format_register_user_portion, - METH_VARARGS, - "register_user_portion($self, portion, /)\n--\n\nRemember a given user-defined binary portion as part of the executable format content." - }, - { - "translate_offset_into_vmpa", py_exe_format_translate_offset_into_vmpa, - METH_VARARGS, - "translate_offset_into_vmpa($self, off, /)\n--\n\nTranslate a physical offset to a full location." - }, - { - "translate_address_into_vmpa", py_exe_format_translate_address_into_vmpa, - METH_VARARGS, - "translate_address_into_vmpa($self, addr, /)\n--\n\nTranslate a physical offset to a full location." - }, + static PyMethodDef py_executable_format_methods[] = { + EXECUTABLE_FORMAT_GET_TARGET_MACHINE_WRAPPER, + EXECUTABLE_FORMAT_GET_MAIN_ADDRESS_WRAPPER, + EXECUTABLE_FORMAT_REFINE_PORTIONS_WRAPPER, + EXECUTABLE_FORMAT_INCLUDE_PORTION_METHOD, + EXECUTABLE_FORMAT_TRANSLATE_OFFSET_INTO_VMPA_METHOD, + EXECUTABLE_FORMAT_TRANSLATE_ADDRESS_INTO_VMPA_METHOD, { NULL } }; - static PyGetSetDef py_exe_format_getseters[] = { + static PyGetSetDef py_executable_format_getseters[] = { + EXECUTABLE_FORMAT_TARGET_MACHINE_ATTRIB, + EXECUTABLE_FORMAT_MAIN_ADDRESS_ATTRIB, + EXECUTABLE_FORMAT_PORTIONS_ATTRIB, { NULL } }; - static PyTypeObject py_exe_format_type = { + static PyTypeObject py_executable_format_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.format.ExeFormat", + .tp_name = "pychrysalide.format.ExecutableFormat", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = "PyChrysalide executable format", + .tp_doc = EXECUTABLE_FORMAT_DOC, - .tp_methods = py_exe_format_methods, - .tp_getset = py_exe_format_getseters, + .tp_methods = py_executable_format_methods, + .tp_getset = py_executable_format_getseters, + + .tp_init = py_executable_format_init, + .tp_new = py_executable_format_new, }; - return &py_exe_format_type; + return &py_executable_format_type; } @@ -265,10 +783,12 @@ bool ensure_python_executable_format_is_registered(void) dict = PyModule_GetDict(module); - if (!ensure_python_binary_format_is_registered()) + if (!ensure_python_program_format_is_registered()) return false; - if (!register_class_for_pygobject(dict, G_TYPE_EXE_FORMAT, type)) + pyg_register_class_init(G_TYPE_EXECUTABLE_FORMAT, (PyGClassInitFunc)py_executable_format_init_gclass); + + if (!register_class_for_pygobject(dict, G_TYPE_EXECUTABLE_FORMAT, type)) return false; } @@ -309,7 +829,7 @@ int convert_to_executable_format(PyObject *arg, void *dst) break; case 1: - *((GExeFormat **)dst) = G_EXE_FORMAT(pygobject_get(arg)); + *((GExecutableFormat **)dst) = G_EXECUTABLE_FORMAT(pygobject_get(arg)); break; default: @@ -328,7 +848,7 @@ int convert_to_executable_format(PyObject *arg, void *dst) /* TRADUCTION D'EMPLACEMENT */ /* ---------------------------------------------------------------------------------- */ - +#if 0 /****************************************************************************** * * * Paramètres : obj = objet Python à convertir en emplacement. * @@ -391,3 +911,4 @@ int convert_to_vmpa_using_executable(PyObject *obj, exe_cv_info_t *info) return result; } +#endif diff --git a/plugins/pychrysalide/format/executable.h b/plugins/pychrysalide/format/executable.h index 9ef1fe5..1b305a4 100644 --- a/plugins/pychrysalide/format/executable.h +++ b/plugins/pychrysalide/format/executable.h @@ -50,7 +50,7 @@ int convert_to_executable_format(PyObject *, void *); /* ---------------------------- TRADUCTION D'EMPLACEMENT ---------------------------- */ - +#if 0 /* Informations utiles à une traduction */ typedef struct _exe_cv_info_t { @@ -64,7 +64,7 @@ typedef struct _exe_cv_info_t /* Réalise une conversion d'un objet Python en localisation. */ int convert_to_vmpa_using_executable(PyObject *, exe_cv_info_t *); - +#endif #endif /* _PLUGINS_PYCHRYSALIDE_FORMAT_EXECUTABLE_H */ diff --git a/plugins/pychrysalide/format/flat.c b/plugins/pychrysalide/format/flat.c index 4df3646..81f0dba 100644 --- a/plugins/pychrysalide/format/flat.c +++ b/plugins/pychrysalide/format/flat.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * flat.c - équivalent Python du fichier "format/flat.c" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,43 +28,45 @@ #include <pygobject.h> -#include <format/flat.h> +#include <format/flat-int.h> #include "executable.h" #include "../access.h" +#include "../constants.h" #include "../helpers.h" #include "../analysis/content.h" -/* Crée un nouvel objet Python de type 'FlatFormat'. */ -static PyObject *py_flat_format_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(flat_format, G_TYPE_FLAT_FORMAT); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_flat_format_init(PyObject *, PyObject *, PyObject *); /****************************************************************************** * * -* Paramètres : type = type de l'objet à instancier. * +* Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * * * -* Description : Crée un nouvel objet Python de type 'FlatFormat'. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : Instance Python mise en place. * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_flat_format_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_flat_format_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *result; /* Instance à retourner */ GBinContent *content; /* Instance GLib du contenu */ const char *machine; /* Identifiant d'architecture */ - unsigned int endian; /* Boutisme de l'architecture */ + SourceEndian endian; /* Boutisme de l'architecture */ int ret; /* Bilan de lecture des args. */ - GExeFormat *format; /* Création GLib à transmettre */ + GFlatFormat *format; /* Création GLib à transmettre */ #define FLAT_FORMAT_DOC \ "FlatFormat is suitable for all executable contents without a proper" \ @@ -74,28 +76,32 @@ static PyObject *py_flat_format_new(PyTypeObject *type, PyObject *args, PyObject "\n" \ " FlatFormat(content, machine, endian)" \ "\n" \ - "Where content is a pychrysalide.analysis.BinContent object, machine" \ - " defines the target architecture and endian provides the right" \ - " endianness of the data." + "Where *content* is a pychrysalide.analysis.BinContent object," \ + " *machine* defines the target architecture as a string value and" \ + " *endian* provides the right endianness of the data, as a" \ + " pychrysalide.SourceEndian value." - ret = PyArg_ParseTuple(args, "O&sI", convert_to_binary_content, &content, &machine, &endian); - if (!ret) return NULL; + /* Récupération des paramètres */ - format = g_flat_format_new(content, machine, endian); + ret = PyArg_ParseTuple(args, "O&sO&", + convert_to_binary_content, &content, + &machine, + convert_to_source_endian, &endian); + if (!ret) return -1; - if (format == NULL) - { - result = Py_None; - Py_INCREF(result); - } + /* Initialisation d'un objet GLib */ - else - { - result = pygobject_new(G_OBJECT(format)); - g_object_unref(format); - } + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - return result; + /* Eléments de base */ + + format = G_FLAT_FORMAT(pygobject_get(self)); + + if (!g_flat_format_create(format, content, machine, endian)) + return -1; + + return 0; } @@ -135,7 +141,9 @@ PyTypeObject *get_python_flat_format_type(void) .tp_methods = py_flat_format_methods, .tp_getset = py_flat_format_getseters, - .tp_new = py_flat_format_new + + .tp_init = py_flat_format_init, + .tp_new = py_flat_format_new, }; diff --git a/plugins/pychrysalide/format/format.h b/plugins/pychrysalide/format/format.h deleted file mode 100644 index 04ac9e3..0000000 --- a/plugins/pychrysalide/format/format.h +++ /dev/null @@ -1,59 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * format.h - prototypes pour l'équivalent Python du fichier "format/format.h" - * - * Copyright (C) 2018-2020 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#ifndef _PLUGINS_PYCHRYSALIDE_FORMAT_FORMAT_H -#define _PLUGINS_PYCHRYSALIDE_FORMAT_FORMAT_H - - -#include <Python.h> -#include <stdbool.h> - - - -/* ------------------------ PARCOURS DE SYMBOLES DE BINAIRES ------------------------ */ - - -/* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_binary_symbol_iterator_type(void); - -/* Prend en charge l'objet 'pychrysalide...BinSymbolIterator'. */ -bool ensure_python_binary_symbol_iterator_is_registered(void); - - - -/* ---------------------------- FORMAT BINAIRE GENERIQUE ---------------------------- */ - - -/* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_binary_format_type(void); - -/* Prend en charge l'objet 'pychrysalide.format.BinFormat'. */ -bool ensure_python_binary_format_is_registered(void); - -/* Tente de convertir en format de binaire. */ -int convert_to_binary_format(PyObject *, void *); - - - -#endif /* _PLUGINS_PYCHRYSALIDE_FORMAT_FORMAT_H */ diff --git a/plugins/pychrysalide/format/known.c b/plugins/pychrysalide/format/known.c index 3167ba2..856c087 100644 --- a/plugins/pychrysalide/format/known.c +++ b/plugins/pychrysalide/format/known.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * known.c - équivalent Python du fichier "format/known.c" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -30,7 +30,6 @@ #include <i18n.h> #include <format/known-int.h> -#include <plugins/dt.h> #include "../access.h" @@ -42,11 +41,10 @@ /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_known_format_new(PyTypeObject *, PyObject *, PyObject *); - /* Initialise la classe des descriptions de fichier binaire. */ -static void py_known_format_init_gclass(GKnownFormatClass *, gpointer); +static int py_known_format_init_gclass(GKnownFormatClass *, PyTypeObject *); + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_known_format_init(PyObject *, PyObject *, PyObject *); @@ -58,10 +56,7 @@ static char *py_known_format_get_key_wrapper(const GKnownFormat *); static char *py_known_format_get_description_wrapper(const GKnownFormat *); /* Assure l'interprétation d'un format en différé. */ -static bool py_known_format_analyze_wrapper(GKnownFormat *, wgroup_id_t, GtkStatusStack *); - -/* Réalise un traitement post-désassemblage. */ -static void py_known_format_complete_analysis_wrapper(GKnownFormat *, wgroup_id_t, GtkStatusStack *); +static bool py_known_format_analyze_wrapper(GKnownFormat *); @@ -71,9 +66,6 @@ static void py_known_format_complete_analysis_wrapper(GKnownFormat *, wgroup_id_ /* Assure l'interprétation d'un format en différé. */ static PyObject *py_known_format_analyze(PyObject *, PyObject *); -/* Réalise un traitement post-désassemblage. */ -static PyObject *py_known_format_complete_analysis(PyObject *, PyObject *); - /* Indique la désignation interne du format. */ static PyObject *py_known_format_get_key(PyObject *, void *); @@ -92,87 +84,25 @@ static PyObject *py_known_format_get_content(PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * -* Description : Accompagne la création d'une instance dérivée en Python. * +* Description : Initialise la classe des formats connus. * * * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_known_format_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_known_format_init_gclass(GKnownFormatClass *gclass, PyTypeObject *pyclass) { - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_known_format_type(); - - if (type == base) - { - result = NULL; - PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); - goto exit; - } - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_KNOWN_FORMAT, type->tp_name, - (GClassInitFunc)py_known_format_init_gclass, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } + PY_CLASS_SET_WRAPPER(gclass->get_key, py_known_format_get_key_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_desc, py_known_format_get_description_wrapper); - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * -* * -* Description : Initialise la classe générique des processeurs. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + PY_CLASS_SET_WRAPPER(gclass->analyze, py_known_format_analyze_wrapper); -static void py_known_format_init_gclass(GKnownFormatClass *class, gpointer unused) -{ - class->get_key = py_known_format_get_key_wrapper; - class->get_desc = py_known_format_get_description_wrapper; - - class->analyze = py_known_format_analyze_wrapper; - class->complete = py_known_format_complete_analysis_wrapper; + return 0; } @@ -201,18 +131,11 @@ static int py_known_format_init(PyObject *self, PyObject *args, PyObject *kwds) "KnownFormat is a small class providing basic features for" \ " recognized formats.\n" \ "\n" \ - "One item has to be defined as class attribute in the final" \ - " class:\n" \ - "* *_key*: a string providing a small name used to identify the" \ - " format.\n" \ - "\n" \ "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.format.KnownFormat._get_key();\n" \ "* pychrysalide.format.KnownFormat._get_description();\n" \ "* pychrysalide.format.KnownFormat._analyze().\n" \ "\n" \ - "The following method may also be defined for new classes too:\n" \ - "* pychrysalide.format.KnownFormat._complete_analysis().\n" \ - "\n" \ "Calls to the *__init__* constructor of this abstract object expect"\ " only one argument: a binary content, provided as a" \ " pychrysalide.analysis.BinContent instance." @@ -231,7 +154,8 @@ static int py_known_format_init(PyObject *self, PyObject *args, PyObject *kwds) format = G_KNOWN_FORMAT(pygobject_get(self)); - g_known_format_set_content(format, content); + if (!g_known_format_create(format, content)) + return -1; return 0; @@ -255,27 +179,39 @@ static char *py_known_format_get_key_wrapper(const GKnownFormat *format) char *result; /* Désignation à renvoyer */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *pykey; /* Clef en objet Python */ + PyObject *pyret; /* Valeur retournée */ int ret; /* Bilan d'une conversion */ +#define KNOWN_FORMAT_GET_KEY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_key, "$self", \ + METH_NOARGS, \ + "Abstract method used to define the internal name" \ + " of the known format.\n" \ + "\n" \ + "The return value has to be a (tiny) string." \ +) + result = NULL; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(format)); - if (PyObject_HasAttrString(pyobj, "_key")) + if (has_python_method(pyobj, "_get_key")) { - pykey = PyObject_GetAttrString(pyobj, "_key"); + pyret = run_python_method(pyobj, "_get_key", NULL); - if (pykey != NULL) + if (pyret != NULL) { - ret = PyUnicode_Check(pykey); + ret = PyUnicode_Check(pyret); if (ret) - result = strdup(PyUnicode_AsUTF8(pykey)); + result = strdup(PyUnicode_AsUTF8(pyret)); + else + PyErr_SetString(PyExc_ValueError, _("unexpected value type for known format key")); - Py_DECREF(pykey); + Py_DECREF(pyret); } @@ -335,6 +271,8 @@ static char *py_known_format_get_description_wrapper(const GKnownFormat *format) if (ret) result = strdup(PyUnicode_AsUTF8(pyret)); + else + PyErr_SetString(PyExc_ValueError, _("unexpected value type for known format description")); Py_DECREF(pyret); @@ -354,8 +292,6 @@ static char *py_known_format_get_description_wrapper(const GKnownFormat *format) /****************************************************************************** * * * Paramètres : format = format chargé dont l'analyse est lancée. * -* gid = groupe de travail dédié. * -* status = barre de statut à tenir informée. * * * * Description : Assure l'interprétation d'un format en différé. * * * @@ -365,12 +301,11 @@ static char *py_known_format_get_description_wrapper(const GKnownFormat *format) * * ******************************************************************************/ -static bool py_known_format_analyze_wrapper(GKnownFormat *format, wgroup_id_t gid, GtkStatusStack *status) +static bool py_known_format_analyze_wrapper(GKnownFormat *format) { bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan d'exécution */ #define KNOWN_FORMAT_ANALYZE_WRAPPER PYTHON_WRAPPER_DEF \ @@ -380,12 +315,8 @@ static bool py_known_format_analyze_wrapper(GKnownFormat *format, wgroup_id_t gi "Abstract method used to start the analysis of the known" \ " format and return its status.\n" \ "\n" \ - "The identifier refers to the working queue used to process" \ - " the analysis. A reference to the main status bar may also be" \ - " provided, as a pychrysalide.gtkext.StatusStack instance if" \ - " running in graphical mode or None otherwise.\n" \ - "\n" \ - "The expected result of the call is a boolean." \ + "The expected result of the call is a boolean value: *True* in" \ + " case of success, *False* in case of failure." \ ) result = false; @@ -396,16 +327,10 @@ static bool py_known_format_analyze_wrapper(GKnownFormat *format, wgroup_id_t gi if (has_python_method(pyobj, "_analyze")) { - args = PyTuple_New(2); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(gid)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(status))); - - pyret = run_python_method(pyobj, "_analyze", args); + pyret = run_python_method(pyobj, "_analyze", NULL); result = (pyret == Py_True); - Py_DECREF(args); Py_XDECREF(pyret); } @@ -419,64 +344,6 @@ static bool py_known_format_analyze_wrapper(GKnownFormat *format, wgroup_id_t gi } -/****************************************************************************** -* * -* Paramètres : format = format chargé dont l'analyse est lancée. * -* gid = groupe de travail dédié. * -* status = barre de statut à tenir informée. * -* * -* Description : Réalise un traitement post-désassemblage. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_known_format_complete_analysis_wrapper(GKnownFormat *format, wgroup_id_t gid, GtkStatusStack *status) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define KNOWN_FORMAT_COMPLETE_ANALYSIS_WRAPPER PYTHON_VOID_WRAPPER_DEF \ -( \ - _complete_analysis, "$self, gid, status, /", \ - METH_VARARGS, \ - "Abstract method used to complete an analysis of a known format.\n" \ - "\n" \ - "The identifier refers to the working queue used to process the" \ - " analysis. A reference to the main status bar may also be" \ - " provided, as a pychrysalide.gtkext.StatusStack instance if" \ - " running in graphical mode or None otherwise.\n" \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(format)); - - if (has_python_method(pyobj, "_complete_analysis")) - { - args = PyTuple_New(2); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(gid)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(status))); - - pyret = run_python_method(pyobj, "_complete_analysis", args); - - Py_DECREF(args); - Py_XDECREF(pyret); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - /* ---------------------------------------------------------------------------------- */ /* DEFINITION DU FORMAT CONNU */ @@ -499,33 +366,24 @@ static void py_known_format_complete_analysis_wrapper(GKnownFormat *format, wgro static PyObject *py_known_format_analyze(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ GKnownFormat *format; /* Format connu manipulé */ bool status; /* Bilan de l'opération */ #define KNOWN_FORMAT_ANALYZE_METHOD PYTHON_METHOD_DEF \ ( \ - analyze, "$self, gid, status, /", \ - METH_VARARGS, py_known_format, \ + analyze, "$self", \ + METH_NOARGS, py_known_format, \ "Start the analysis of the known format and return its status." \ "\n" \ "Once this analysis is done, a few early symbols and the" \ " mapped sections are expected to be defined, if any.\n" \ "\n" \ - "The identifier refers to the working queue used to process" \ - " the analysis. A reference to the main status bar may also be" \ - " provided, as a pychrysalide.gtkext.StatusStack instance if" \ - " running in graphical mode or None otherwise.\n" \ - "\n" \ "The return value is a boolean status of the operation." \ ) - ret = PyArg_ParseTuple(args, "");//|KO!", &gid, &status); - if (!ret) return NULL; - format = G_KNOWN_FORMAT(pygobject_get(self)); - status = g_known_format_analyze(format, 0, NULL); + status = g_known_format_analyze(format); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -537,57 +395,6 @@ static PyObject *py_known_format_analyze(PyObject *self, PyObject *args) /****************************************************************************** * * -* Paramètres : self = objet représentant un format connu. * -* args = arguments fournis pour l'opération. * -* * -* Description : Réalise un traitement post-désassemblage. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_known_format_complete_analysis(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GKnownFormat *format; /* Format connu manipulé */ - -#define KNOWN_FORMAT_COMPLETE_ANALYSIS_METHOD PYTHON_METHOD_DEF \ -( \ - complete_analysis, "$self, gid, status, /", \ - METH_VARARGS, py_known_format, \ - "Complete an analysis of a known format.\n" \ - "\n" \ - "This process is usually done once the disassembling process" \ - " is completed.\n" \ - "\n" \ - "The identifier refers to the working queue used to process" \ - " the analysis. A reference to the main status bar may also be" \ - " provided, as a pychrysalide.gtkext.StatusStack instance if" \ - " running in graphical mode or None otherwise.\n" \ - "\n" \ - "The return value is a boolean status of the operation." \ -) - - ret = PyArg_ParseTuple(args, "");//|KO!", &gid, &status); - if (!ret) return NULL; - - format = G_KNOWN_FORMAT(pygobject_get(self)); - - g_known_format_complete_analysis(format, 0, NULL); - - result = Py_None; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * @@ -615,9 +422,30 @@ static PyObject *py_known_format_get_key(PyObject *self, void *closure) key = g_known_format_get_key(format); - result = PyUnicode_FromString(key); + if (key != NULL) + { + result = PyUnicode_FromString(key); + free(key); + } + else + { + result = NULL; - free(key); + /** + * Si l'absence de retour vient d'une instance implémentée en Python, + * une exception peut (et doit dans ce cas) avoir été définie dans la + * fonction py_known_format_get_key_wrapper(). + * + * Aucun traitement supplémntaire n'est alors nécessaire. + * + * Une action est cependant nécessaire pour assurer un traitement + * des extensions purement natives offrant une mauvaise implémentation. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_ValueError, _("unexpected NULL value for known format key")); + + } return result; @@ -653,9 +481,30 @@ static PyObject *py_known_format_get_description(PyObject *self, void *closure) desc = g_known_format_get_description(format); - result = PyUnicode_FromString(desc); + if (desc != NULL) + { + result = PyUnicode_FromString(desc); + free(desc); + } + else + { + result = NULL; - free(desc); + /** + * Si l'absence de retour vient d'une instance implémentée en Python, + * une exception peut (et doit dans ce cas) avoir été définie dans la + * fonction py_known_format_get_description_wrapper(). + * + * Aucun traitement supplémntaire n'est alors nécessaire. + * + * Une action est cependant nécessaire pour assurer un traitement + * des extensions purement natives offrant une mauvaise implémentation. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_ValueError, _("unexpected NULL value for known format description")); + + } return result; @@ -724,11 +573,10 @@ static PyObject *py_known_format_get_content(PyObject *self, void *closure) PyTypeObject *get_python_known_format_type(void) { static PyMethodDef py_known_format_methods[] = { + KNOWN_FORMAT_GET_KEY_WRAPPER, KNOWN_FORMAT_GET_DESCRIPTION_WRAPPER, KNOWN_FORMAT_ANALYZE_WRAPPER, - KNOWN_FORMAT_COMPLETE_ANALYSIS_WRAPPER, KNOWN_FORMAT_ANALYZE_METHOD, - KNOWN_FORMAT_COMPLETE_ANALYSIS_METHOD, { NULL } }; @@ -789,6 +637,8 @@ bool ensure_python_known_format_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_KNOWN_FORMAT, (PyGClassInitFunc)py_known_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/format/module.c b/plugins/pychrysalide/format/module.c index 62c15ed..95d4172 100644 --- a/plugins/pychrysalide/format/module.c +++ b/plugins/pychrysalide/format/module.c @@ -30,12 +30,12 @@ #include "executable.h" #include "flat.h" -#include "format.h" #include "known.h" -#include "preload.h" -#include "strsym.h" -#include "symbol.h" -#include "symiter.h" +//#include "preload.h" +#include "program.h" +//#include "strsym.h" +//#include "symbol.h" +//#include "symiter.h" #include "../helpers.h" @@ -105,11 +105,11 @@ bool populate_format_module(void) if (result) result = ensure_python_executable_format_is_registered(); if (result) result = ensure_python_flat_format_is_registered(); if (result) result = ensure_python_known_format_is_registered(); - if (result) result = ensure_python_binary_format_is_registered(); - if (result) result = ensure_python_preload_info_is_registered(); - if (result) result = ensure_python_string_symbol_is_registered(); - if (result) result = ensure_python_binary_symbol_is_registered(); - if (result) result = ensure_python_sym_iterator_is_registered(); + //if (result) result = ensure_python_preload_info_is_registered(); + if (result) result = ensure_python_program_format_is_registered(); + //if (result) result = ensure_python_string_symbol_is_registered(); + //if (result) result = ensure_python_binary_symbol_is_registered(); + //if (result) result = ensure_python_sym_iterator_is_registered(); assert(result); diff --git a/plugins/pychrysalide/format/format.c b/plugins/pychrysalide/format/program.c index 82c6c33..57a359a 100644 --- a/plugins/pychrysalide/format/format.c +++ b/plugins/pychrysalide/format/program.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * format.c - équivalent Python du fichier "format/format.c" + * program.c - équivalent Python du fichier "format/program.c" * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,101 +22,115 @@ */ -#include "format.h" +#include "program.h" #include <pygobject.h> -#include <i18n.h> -#include <format/format-int.h> -#include <plugins/dt.h> +#include <format/program-int.h> +#include "known.h" +#include "../access.h" +#include "../constants.h" +#include "../helpers.h" +#include "../analysis/content.h" +#include "../arch/vmpa.h" + + +/* #include "constants.h" #include "executable.h" -#include "known.h" #include "symbol.h" #include "symiter.h" -#include "../access.h" -#include "../helpers.h" #include "../analysis/constants.h" #include "../analysis/content.h" -#include "../arch/vmpa.h" #include "../arch/constants.h" +*/ /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_binary_format_new(PyTypeObject *, PyObject *, PyObject *); +/* Initialise la classe des formats de programmes. */ +static int py_program_format_init_gclass(GProgramFormatClass *, PyTypeObject *); -/* Initialise la classe des descriptions de fichier binaire. */ -static void py_binary_format_init_gclass(GBinFormatClass *, gpointer); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ -static int py_binary_format_init(PyObject *, PyObject *, PyObject *); +static int py_program_format_init(PyObject *, PyObject *, PyObject *); /* Indique le boutisme employé par le format binaire analysé. */ -static SourceEndian py_binary_format_get_endianness_wrapper(const GBinFormat *); +static SourceEndian py_program_format_get_endianness_wrapper(const GProgramFormat *); + +/* Fournit l'emplacement d'une section donnée. */ +static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *); /* ---------------------------- FORMAT BINAIRE GENERIQUE ---------------------------- */ +#if 0 /* Ajoute une information complémentaire à un format. */ -static PyObject *py_binary_format_set_flag(PyObject *, PyObject *); +static PyObject *py_program_format_set_flag(PyObject *, PyObject *); /* Retire une information complémentaire à un format. */ -static PyObject *py_binary_format_unset_flag(PyObject *, PyObject *); +static PyObject *py_program_format_unset_flag(PyObject *, PyObject *); /* Détermine si un format possède un fanion particulier. */ -static PyObject *py_binary_format_has_flag(PyObject *, PyObject *); +static PyObject *py_program_format_has_flag(PyObject *, PyObject *); +#endif + + +/* Fournit l'emplacement d'une section donnée. */ +static PyObject *py_program_format_find_section_range_by_name(PyObject *, PyObject *); + +#if 0 /* Enregistre une adresse comme début d'une zone de code. */ -static PyObject *py_binary_format_register_code_point(PyObject *, PyObject *); +static PyObject *py_program_format_register_code_point(PyObject *, PyObject *); /* Ajoute un symbole à la collection du format binaire. */ -static PyObject *py_binary_format_add_symbol(PyObject *, PyObject *); +static PyObject *py_program_format_add_symbol(PyObject *, PyObject *); /* Retire un symbole de la collection du format binaire. */ -static PyObject *py_binary_format_remove_symbol(PyObject *, PyObject *); +static PyObject *py_program_format_remove_symbol(PyObject *, PyObject *); /* Recherche le symbole correspondant à une étiquette. */ -static PyObject *py_binary_format_find_symbol_by_label(PyObject *, PyObject *); +static PyObject *py_program_format_find_symbol_by_label(PyObject *, PyObject *); /* Recherche le symbole suivant celui lié à une adresse. */ -static PyObject *py_binary_format_find_symbol_at(PyObject *, PyObject *); +static PyObject *py_program_format_find_symbol_at(PyObject *, PyObject *); /* Recherche le symbole suivant celui lié à une adresse. */ -static PyObject *py_binary_format_find_next_symbol_at(PyObject *, PyObject *); +static PyObject *py_program_format_find_next_symbol_at(PyObject *, PyObject *); /* Recherche le symbole correspondant à une adresse. */ -static PyObject *py_binary_format_resolve_symbol(PyObject *, PyObject *); +static PyObject *py_program_format_resolve_symbol(PyObject *, PyObject *); /* Fournit les particularités du format. */ -static PyObject *py_binary_format_get_flags(PyObject *, void *); +static PyObject *py_program_format_get_flags(PyObject *, void *); /* Indique le boutisme employé par le format binaire analysé. */ -static PyObject *py_binary_format_get_endianness(PyObject *, void *); +static PyObject *py_program_format_get_endianness(PyObject *, void *); /* Fournit la liste de tous les symboles détectés. */ -static PyObject *py_binary_format_get_symbols(PyObject *, void *); - +static PyObject *py_program_format_get_symbols(PyObject *, void *); +#endif /* ------------------ CONSERVATION DES SOUCIS DURANT LE CHARGEMENT ------------------ */ - +#if 0 /* Etend la liste des soucis détectés avec de nouvelles infos. */ -static PyObject *py_binary_format_add_error(PyObject *, PyObject *); +static PyObject *py_program_format_add_error(PyObject *, PyObject *); /* Fournit les éléments concernant tous les soucis détectés. */ -static PyObject *py_binary_format_get_errors(PyObject *, void *); - +static PyObject *py_program_format_get_errors(PyObject *, void *); +#endif /* ---------------------------------------------------------------------------------- */ @@ -126,83 +140,23 @@ static PyObject *py_binary_format_get_errors(PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * -* Description : Accompagne la création d'une instance dérivée en Python. * +* Description : Initialise la classe des formats de programmes. * * * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_binary_format_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_program_format_init_gclass(GProgramFormatClass *gclass, PyTypeObject *pyclass) { - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_known_format_type(); - - if (type == base) - { - result = NULL; - PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); - goto exit; - } - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_BIN_FORMAT, type->tp_name, - (GClassInitFunc)py_binary_format_init_gclass, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - result = PyType_GenericNew(type, args, kwds); - - exit: + PY_CLASS_SET_WRAPPER(gclass->get_endian, py_program_format_get_endianness_wrapper); + PY_CLASS_SET_WRAPPER(gclass->find_range_by_name, py_program_format_find_section_range_by_name_wrapper); - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * -* * -* Description : Initialise la classe générique des processeurs. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_binary_format_init_gclass(GBinFormatClass *class, gpointer unused) -{ - class->get_endian = py_binary_format_get_endianness_wrapper; + return 0; } @@ -221,27 +175,44 @@ static void py_binary_format_init_gclass(GBinFormatClass *class, gpointer unused * * ******************************************************************************/ -static int py_binary_format_init(PyObject *self, PyObject *args, PyObject *kwds) +static int py_program_format_init(PyObject *self, PyObject *args, PyObject *kwds) { + GBinContent *content; /* Contenu à intégrer au format*/ int ret; /* Bilan de lecture des args. */ - -#define BINARY_FORMAT_DOC \ - "The BinFormat class is the major poart of binary format support." \ - " It is the core class used by loading most of the binary files.\n" \ - "\n" \ - "One item has to be defined as class attribute in the final" \ - " class:\n" \ - "* *_endianness*: a pychrysalide.analysis.BinContent.SourceEndian" \ - " value indicating the endianness of the format.\n" \ - "\n" \ - "Calls to the *__init__* constructor of this abstract object expect"\ - " no particular argument." + GProgramFormat *format; /* Format à manipuler */ + +#define PROGRAM_FORMAT_DOC \ + "The ProgramFormat class is the major part of binary format" \ + " support. It is the core class used by loading most of the binary" \ + " files.\n" \ + "\n" \ + "The following method has to be defined for new classes:\n" \ + "* pychrysalide.format.ProgramFormat._get_endianness().\n" \ + "\n" \ + "Other optional method may be defined for new classes:\n" \ + "* pychrysalide.format.ProgramFormat._find_section_range_by_name().\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect" \ + " only one argument: a binary content, provided as a" \ + " pychrysalide.analysis.BinContent instance." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&", convert_to_binary_content, &content); + if (!ret) return -1; /* Initialisation d'un objet GLib */ ret = forward_pygobjet_init(self); if (ret == -1) return -1; + /* Eléments de base */ + + format = G_PROGRAM_FORMAT(pygobject_get(self)); + + if (!g_program_format_create(format, content)) + return -1; + return 0; } @@ -259,7 +230,7 @@ static int py_binary_format_init(PyObject *self, PyObject *args, PyObject *kwds) * * ******************************************************************************/ -static SourceEndian py_binary_format_get_endianness_wrapper(const GBinFormat *format) +static SourceEndian py_program_format_get_endianness_wrapper(const GProgramFormat *format) { SourceEndian result; /* Boutisme à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ @@ -267,22 +238,109 @@ static SourceEndian py_binary_format_get_endianness_wrapper(const GBinFormat *fo PyObject *pyret; /* Valeur retournée */ int ret; /* Bilan d'une conversion */ +#define PROGRAM_FORMAT_GET_ENDIANNESS_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_endianness_wrapper, "$self", \ + METH_NOARGS, \ + "Abstract method used to define the endianness of the format.\n"\ + "\n" \ + "The return value is of type pychrysalide.SourceEndian," \ + " pychrysalide.SourceEndian.LITTLE by default." \ +) + result = SRE_LITTLE; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(format)); - if (PyObject_HasAttrString(pyobj, "_endianness")) + if (has_python_method(pyobj, "_get_endianness")) { - pyret = PyObject_GetAttrString(pyobj, "_endianness"); + pyret = run_python_method(pyobj, "_get_endianness", NULL); if (pyret != NULL) { ret = convert_to_source_endian(pyret, &result); if (ret != 1) + { + PyErr_Clear(); result = SRE_LITTLE; + } + + Py_DECREF(pyret); + + } + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : format = description du programme à consulter. * +* name = nom de la section recherchée. * +* range = emplacement en mémoire à renseigner. [OUT] * +* * +* Description : Fournit l'emplacement d'une section donnée. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Valeur retournée */ + int ret; /* Bilan d'une conversion */ + +#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _find_section_range_by_name_wrapper, "$self, name", \ + METH_VARARGS, \ + "Abstract method used to compute the area of a section identified by" \ + " its name.\n" \ + "\n" \ + "The expected returned value is a pychrysalide.arch.mrange instance or" \ + " *None* in case of failure." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(format)); + + if (has_python_method(pyobj, "_find_section_range_by_name")) + { + pyret = run_python_method(pyobj, "_find_section_range_by_name", NULL); + + if (pyret != NULL) + { + if (pyret == Py_None) + result = false; + + else + { + ret = convert_any_to_mrange(pyret, range); + + result = (ret == 1); + + if (!result) + PyErr_Clear(); + + } Py_DECREF(pyret); @@ -303,7 +361,7 @@ static SourceEndian py_binary_format_get_endianness_wrapper(const GBinFormat *fo /* FORMAT BINAIRE GENERIQUE */ /* ---------------------------------------------------------------------------------- */ - +#if 0 /****************************************************************************** * * * Paramètres : self = serveur à manipuler. * @@ -317,18 +375,18 @@ static SourceEndian py_binary_format_get_endianness_wrapper(const GBinFormat *fo * * ******************************************************************************/ -static PyObject *py_binary_format_set_flag(PyObject *self, PyObject *args) +static PyObject *py_program_format_set_flag(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ unsigned int flag; /* Propriété à traiter */ int ret; /* Bilan de lecture des args. */ - GBinFormat *format; /* Elément à manipuler */ + GProgramFormat *format; /* Elément à manipuler */ bool status; /* Bilan de l'opération */ -#define BINARY_FORMAT_SET_FLAG_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_SET_FLAG_METHOD PYTHON_METHOD_DEF \ ( \ set_flag, "$self, flag, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Add a property from a binary format.\n" \ "\n" \ "This property is one of the values listed in the" \ @@ -341,9 +399,9 @@ static PyObject *py_binary_format_set_flag(PyObject *self, PyObject *args) ret = PyArg_ParseTuple(args, "I", &flag); if (!ret) return NULL; - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); - status = g_binary_format_set_flag(format, flag); + status = g_program_format_set_flag(format, flag); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -366,18 +424,18 @@ static PyObject *py_binary_format_set_flag(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_binary_format_unset_flag(PyObject *self, PyObject *args) +static PyObject *py_program_format_unset_flag(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ unsigned int flag; /* Propriété à traiter */ int ret; /* Bilan de lecture des args. */ - GBinFormat *format; /* Elément à manipuler */ + GProgramFormat *format; /* Elément à manipuler */ bool status; /* Bilan de l'opération */ -#define BINARY_FORMAT_UNSET_FLAG_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_UNSET_FLAG_METHOD PYTHON_METHOD_DEF \ ( \ unset_flag, "$self, flag, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Remove a property from a binary format.\n" \ "\n" \ "This property is one of the values listed in the" \ @@ -390,9 +448,9 @@ static PyObject *py_binary_format_unset_flag(PyObject *self, PyObject *args) ret = PyArg_ParseTuple(args, "I", &flag); if (!ret) return NULL; - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); - status = g_binary_format_unset_flag(format, flag); + status = g_program_format_unset_flag(format, flag); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -415,18 +473,18 @@ static PyObject *py_binary_format_unset_flag(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_binary_format_has_flag(PyObject *self, PyObject *args) +static PyObject *py_program_format_has_flag(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ unsigned int flag; /* Propriété à traiter */ int ret; /* Bilan de lecture des args. */ - GBinFormat *format; /* Elément à manipuler */ + GProgramFormat *format; /* Elément à manipuler */ bool status; /* Bilan de l'opération */ -#define BINARY_FORMAT_HAS_FLAG_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_HAS_FLAG_METHOD PYTHON_METHOD_DEF \ ( \ has_flag, "$self, flag, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Test if a binary format has a given property.\n" \ "\n" \ "This property is one of the values listed in the" \ @@ -438,9 +496,9 @@ static PyObject *py_binary_format_has_flag(PyObject *self, PyObject *args) ret = PyArg_ParseTuple(args, "I", &flag); if (!ret) return NULL; - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); - status = g_binary_format_has_flag(format, flag); + status = g_program_format_has_flag(format, flag); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -448,10 +506,66 @@ static PyObject *py_binary_format_has_flag(PyObject *self, PyObject *args) return result; } +#endif /****************************************************************************** * * +* Paramètres : self = serveur à manipuler. * +* args = arguments d'appel non utilisés ici. * +* * +* Description : Fournit l'emplacement d'une section donnée. * +* * +* Retour : Emplacement ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_program_format_find_section_range_by_name(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + const char *name; /* Nom de section ciblée */ + int ret; /* Bilan de lecture des args. */ + GProgramFormat *format; /* Elément à manipuler */ + mrange_t range; /* Emplacement obtenu ? */ + bool status; /* Bilan de l'opération */ + +#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \ +( \ + find_section_range_by_name, "$self, name, /", \ + METH_VARARGS, py_program_format, \ + "Compute the area of a section identified by its name.\n" \ + "\n" \ + "The *name* argument is a string value.\n" \ + "\n" \ + "The returned value is a pychrysalide.arch.mrange instance or" \ + " *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s", &name); + if (!ret) return NULL; + + format = G_PROGRAM_FORMAT(pygobject_get(self)); + + status = g_program_format_find_section_range_by_name(format, name, &range); + + if (status) + result = build_from_internal_mrange(&range); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + +#if 0 +/****************************************************************************** +* * * Paramètres : self = classe représentant un format. * * args = arguments fournis à l'appel. * * * @@ -463,17 +577,17 @@ static PyObject *py_binary_format_has_flag(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_binary_format_register_code_point(PyObject *self, PyObject *args) +static PyObject *py_program_format_register_code_point(PyObject *self, PyObject *args) { unsigned long long pt; /* Adresse virtuelle du point */ DisassPriorityLevel level; /* Nature du point fourni */ int ret; /* Bilan de lecture des args. */ - GBinFormat *format; /* Format de binaire manipulé */ + GProgramFormat *format; /* Format de binaire manipulé */ -#define BINARY_FORMAT_REGISTER_CODE_POINT_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_REGISTER_CODE_POINT_METHOD PYTHON_METHOD_DEF \ ( \ register_code_point, "$self, point, level, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Register a virtual address as entry point or basic point.\n" \ "\n" \ "The point is an integer value for the virtual memory location" \ @@ -484,9 +598,9 @@ static PyObject *py_binary_format_register_code_point(PyObject *self, PyObject * ret = PyArg_ParseTuple(args, "kO&", &pt, convert_to_disass_priority_level, &level); if (!ret) return NULL; - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); - g_binary_format_register_code_point(format, pt, level); + g_program_format_register_code_point(format, pt, level); Py_RETURN_NONE; @@ -506,18 +620,18 @@ static PyObject *py_binary_format_register_code_point(PyObject *self, PyObject * * * ******************************************************************************/ -static PyObject *py_binary_format_add_symbol(PyObject *self, PyObject *args) +static PyObject *py_program_format_add_symbol(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ GBinSymbol *symbol; /* Enventuel symbole trouvé */ int ret; /* Bilan de lecture des args. */ - GBinFormat *format; /* Format de binaire manipulé */ + GProgramFormat *format; /* Format de binaire manipulé */ bool added; /* Bilan de l'appel interne */ -#define BINARY_FORMAT_ADD_SYMBOL_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_ADD_SYMBOL_METHOD PYTHON_METHOD_DEF \ ( \ add_symbol, "$self, symbol, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Register a new symbol for the format.\n" \ "\n" \ "The symbol has to be a pychrysalide.format.BinSymbol instance." \ @@ -526,10 +640,10 @@ static PyObject *py_binary_format_add_symbol(PyObject *self, PyObject *args) ret = PyArg_ParseTuple(args, "O&", convert_to_binary_symbol, &symbol); if (!ret) return NULL; - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); g_object_ref(G_OBJECT(symbol)); - added = g_binary_format_add_symbol(format, symbol); + added = g_program_format_add_symbol(format, symbol); result = added ? Py_True : Py_False; Py_INCREF(result); @@ -552,17 +666,17 @@ static PyObject *py_binary_format_add_symbol(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_binary_format_remove_symbol(PyObject *self, PyObject *args) +static PyObject *py_program_format_remove_symbol(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ GBinSymbol *symbol; /* Enventuel symbole trouvé */ int ret; /* Bilan de lecture des args. */ - GBinFormat *format; /* Format de binaire manipulé */ + GProgramFormat *format; /* Format de binaire manipulé */ -#define BINARY_FORMAT_REMOVE_SYMBOL_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_REMOVE_SYMBOL_METHOD PYTHON_METHOD_DEF \ ( \ remove_symbol, "$self, symbol, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Unregister a symbol from the format.\n" \ "\n" \ "The symbol has to be a pychrysalide.format.BinSymbol instance." \ @@ -571,9 +685,9 @@ static PyObject *py_binary_format_remove_symbol(PyObject *self, PyObject *args) ret = PyArg_ParseTuple(args, "O&", convert_to_binary_symbol, &symbol); if (!ret) return NULL; - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); - g_binary_format_remove_symbol(format, symbol); + g_program_format_remove_symbol(format, symbol); result = Py_None; Py_INCREF(result); @@ -596,19 +710,19 @@ static PyObject *py_binary_format_remove_symbol(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_binary_format_find_symbol_by_label(PyObject *self, PyObject *args) +static PyObject *py_program_format_find_symbol_by_label(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ const char *label; /* Etiquette à retrouver */ int ret; /* Bilan de lecture des args. */ - GBinFormat *format; /* Format de binaire manipulé */ + GProgramFormat *format; /* Format de binaire manipulé */ GBinSymbol *symbol; /* Enventuel symbole trouvé */ bool found; /* Bilan de la recherche */ -#define BINARY_FORMAT_FIND_SYMBOL_BY_LABEL_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_FIND_SYMBOL_BY_LABEL_METHOD PYTHON_METHOD_DEF \ ( \ find_symbol_by_label, "$self, label, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Find the symbol with a given label, provided as a string.\n" \ "\n" \ "The result is a pychrysalide.format.BinSymbol instance, or None" \ @@ -618,9 +732,9 @@ static PyObject *py_binary_format_find_symbol_by_label(PyObject *self, PyObject ret = PyArg_ParseTuple(args, "s", &label); if (!ret) return NULL; - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); - found = g_binary_format_find_symbol_by_label(format, label, &symbol); + found = g_program_format_find_symbol_by_label(format, label, &symbol); if (found) { @@ -651,19 +765,19 @@ static PyObject *py_binary_format_find_symbol_by_label(PyObject *self, PyObject * * ******************************************************************************/ -static PyObject *py_binary_format_find_symbol_at(PyObject *self, PyObject *args) +static PyObject *py_program_format_find_symbol_at(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ - GBinFormat *format; /* Format de binaire manipulé */ + GProgramFormat *format; /* Format de binaire manipulé */ exe_cv_info_t conv; /* Informations de conversion */ int ret; /* Bilan de lecture des args. */ GBinSymbol *symbol; /* Enventuel symbole trouvé */ bool found; /* Bilan de la recherche */ -#define BINARY_FORMAT_FIND_SYMBOL_AT_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_FIND_SYMBOL_AT_METHOD PYTHON_METHOD_DEF \ ( \ find_symbol_at, "$self, addr, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Find the symbol located at a given address, provided as a" \ " pychrysalide.arch.vmpa instance.\n" \ "\n" \ @@ -671,14 +785,14 @@ static PyObject *py_binary_format_find_symbol_at(PyObject *self, PyObject *args) " None if no symbol was found." \ ) - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); conv.format = G_IS_EXE_FORMAT(format) ? G_EXE_FORMAT(format) : NULL; ret = PyArg_ParseTuple(args, "O&", convert_to_vmpa_using_executable, &conv); if (!ret) return NULL; - found = g_binary_format_find_symbol_at(format, conv.vmpa, &symbol); + found = g_program_format_find_symbol_at(format, conv.vmpa, &symbol); if (found) { @@ -709,19 +823,19 @@ static PyObject *py_binary_format_find_symbol_at(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_binary_format_find_next_symbol_at(PyObject *self, PyObject *args) +static PyObject *py_program_format_find_next_symbol_at(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ - GBinFormat *format; /* Format de binaire manipulé */ + GProgramFormat *format; /* Format de binaire manipulé */ exe_cv_info_t conv; /* Informations de conversion */ int ret; /* Bilan de lecture des args. */ GBinSymbol *symbol; /* Enventuel symbole trouvé */ bool found; /* Bilan de la recherche */ -#define BINARY_FORMAT_FIND_NEXT_SYMBOL_AT_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_FIND_NEXT_SYMBOL_AT_METHOD PYTHON_METHOD_DEF \ ( \ find_next_symbol_at, "$self, addr, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Find the symbol next to the one found at a given address," \ " provided as a pychrysalide.arch.vmpa instance.\n" \ "\n" \ @@ -729,14 +843,14 @@ static PyObject *py_binary_format_find_next_symbol_at(PyObject *self, PyObject * " None if no symbol was found." \ ) - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); conv.format = G_IS_EXE_FORMAT(format) ? G_EXE_FORMAT(format) : NULL; ret = PyArg_ParseTuple(args, "O&", convert_to_vmpa_using_executable, &conv); if (!ret) return NULL; - found = g_binary_format_find_next_symbol_at(format, conv.vmpa, &symbol); + found = g_program_format_find_next_symbol_at(format, conv.vmpa, &symbol); if (found) { @@ -767,10 +881,10 @@ static PyObject *py_binary_format_find_next_symbol_at(PyObject *self, PyObject * * * ******************************************************************************/ -static PyObject *py_binary_format_resolve_symbol(PyObject *self, PyObject *args) +static PyObject *py_program_format_resolve_symbol(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ - GBinFormat *format; /* Format de binaire manipulé */ + GProgramFormat *format; /* Format de binaire manipulé */ exe_cv_info_t conv; /* Informations de conversion */ int strict; /* Tolérance acceptée */ int ret; /* Bilan de lecture des args. */ @@ -778,10 +892,10 @@ static PyObject *py_binary_format_resolve_symbol(PyObject *self, PyObject *args) phys_t diff; /* Décalage éventuel mesuré */ bool found; /* Bilan de la recherche */ -#define BINARY_FORMAT_RESOLVE_SYMBOL_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_RESOLVE_SYMBOL_METHOD PYTHON_METHOD_DEF \ ( \ resolve_symbol, "$self, addr, strict, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Search for a position inside a symbol by a given address.\n" \ "\n" \ "The result is a couple of (pychrysalide.format.BinSymbol," \ @@ -793,14 +907,14 @@ static PyObject *py_binary_format_resolve_symbol(PyObject *self, PyObject *args) " always 0 upon success." \ ) - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); conv.format = G_IS_EXE_FORMAT(format) ? G_EXE_FORMAT(format) : NULL; ret = PyArg_ParseTuple(args, "O&p", convert_to_vmpa_using_executable, &conv, &strict); if (!ret) return NULL; - found = g_binary_format_resolve_symbol(format, conv.vmpa, strict, &symbol, &diff); + found = g_program_format_resolve_symbol(format, conv.vmpa, strict, &symbol, &diff); if (found) { @@ -835,27 +949,28 @@ static PyObject *py_binary_format_resolve_symbol(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_binary_format_get_flags(PyObject *self, void *closure) +static PyObject *py_program_format_get_flags(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ - GBinFormat *format; /* Elément à consulter */ + GProgramFormat *format; /* Elément à consulter */ FormatFlag flags; /* Indications complémentaires */ -#define BINARY_FORMAT_FLAGS_ATTRIB PYTHON_GET_DEF_FULL \ +#define PROGRAM_FORMAT_FLAGS_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - flags, py_binary_format, \ + flags, py_program_format, \ "Provide all the flags set for a format. The return value" \ " is of type pychrysalide.format.BinFormat.FormatFlag." \ ) - format = G_BIN_FORMAT(pygobject_get(self)); - flags = g_binary_format_get_flags(format); + format = G_PROGRAM_FORMAT(pygobject_get(self)); + flags = g_program_format_get_flags(format); - result = cast_with_constants_group_from_type(get_python_binary_format_type(), "FormatFlag", flags); + result = cast_with_constants_group_from_type(get_python_program_format_type(), "FormatFlag", flags); return result; } +#endif /****************************************************************************** @@ -871,29 +986,31 @@ static PyObject *py_binary_format_get_flags(PyObject *self, void *closure) * * ******************************************************************************/ -static PyObject *py_binary_format_get_endianness(PyObject *self, void *closure) +static PyObject *py_program_format_get_endianness(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ - GBinFormat *format; /* Elément à consulter */ + GProgramFormat *format; /* Elément à consulter */ SourceEndian endianness; /* Boutisme du format */ -#define BINARY_FORMAT_ENDIANNESS_ATTRIB PYTHON_GET_DEF_FULL \ +#define PROGRAM_FORMAT_ENDIANNESS_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - endianness, py_binary_format, \ + endianness, py_program_format, \ "Endianness of the format. The return value is of type" \ - " pychrysalide.analysis.BinContent.SourceEndian." \ + " pychrysalide.SourceEndian." \ ) - format = G_BIN_FORMAT(pygobject_get(self)); - endianness = g_binary_format_get_endianness(format); + format = G_PROGRAM_FORMAT(pygobject_get(self)); + endianness = g_program_format_get_endianness(format); - result = cast_with_constants_group_from_type(get_python_binary_content_type(), "SourceEndian", endianness); + result = cast_with_constants_group_from_module("pychrysalide", "SourceEndian", endianness); return result; } +#if 0 + /****************************************************************************** * * * Paramètres : self = classe représentant un format binaire. * @@ -907,15 +1024,15 @@ static PyObject *py_binary_format_get_endianness(PyObject *self, void *closure) * * ******************************************************************************/ -static PyObject *py_binary_format_get_symbols(PyObject *self, void *closure) +static PyObject *py_program_format_get_symbols(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ PyTypeObject *iterator_type; /* Type Python de l'itérateur */ PyObject *args; /* Liste des arguments d'appel */ -#define BINARY_FORMAT_SYMBOLS_ATTRIB PYTHON_GET_DEF_FULL \ +#define PROGRAM_FORMAT_SYMBOLS_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - symbols, py_binary_format, \ + symbols, py_program_format, \ "Iterable list of all symbols found in the binary format.\n" \ "\n" \ "The returned iterator is a pychrysalide.format.SymIterator" \ @@ -935,13 +1052,15 @@ static PyObject *py_binary_format_get_symbols(PyObject *self, void *closure) } +#endif + /* ---------------------------------------------------------------------------------- */ /* CONSERVATION DES SOUCIS DURANT LE CHARGEMENT */ /* ---------------------------------------------------------------------------------- */ - +#if 0 /****************************************************************************** * * * Paramètres : self = architecture concernée par la procédure. * @@ -955,18 +1074,18 @@ static PyObject *py_binary_format_get_symbols(PyObject *self, void *closure) * * ******************************************************************************/ -static PyObject *py_binary_format_add_error(PyObject *self, PyObject *args) +static PyObject *py_program_format_add_error(PyObject *self, PyObject *args) { BinaryFormatError type; /* Type d'erreur détectée */ vmpa2t *addr; /* Position d'une erreur */ const char *desc; /* Description d'une erreur */ int ret; /* Bilan de lecture des args. */ - GBinFormat *format; /* Format binaire manipulé */ + GProgramFormat *format; /* Format binaire manipulé */ -#define BINARY_FORMAT_ADD_ERROR_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_ADD_ERROR_METHOD PYTHON_METHOD_DEF \ ( \ add_error, "$self, type, addr, desc, /", \ - METH_VARARGS, py_binary_format, \ + METH_VARARGS, py_program_format, \ "Extend the list of detected errors linked to the format.\n" \ "\n" \ "The type of error has to be one of the" \ @@ -976,12 +1095,12 @@ static PyObject *py_binary_format_add_error(PyObject *self, PyObject *args) " what has failed." \ ) - ret = PyArg_ParseTuple(args, "O&O&s", convert_to_binary_format_error, &type, convert_any_to_vmpa, &addr, &desc); + ret = PyArg_ParseTuple(args, "O&O&s", convert_to_program_format_error, &type, convert_any_to_vmpa, &addr, &desc); if (!ret) return NULL; - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); - g_binary_format_add_error(format, type, addr, desc); + g_program_format_add_error(format, type, addr, desc); clean_vmpa_arg(addr); @@ -1003,10 +1122,10 @@ static PyObject *py_binary_format_add_error(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_binary_format_get_errors(PyObject *self, void *closure) +static PyObject *py_program_format_get_errors(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GBinFormat *format; /* Format binaire manipulé */ + GProgramFormat *format; /* Format binaire manipulé */ size_t count; /* Nombre d'éléments à traiter */ size_t i; /* Boucle de parcours */ #ifndef NDEBUG @@ -1018,9 +1137,9 @@ static PyObject *py_binary_format_get_errors(PyObject *self, void *closure) PyObject *py_type; /* Version Python du type */ PyObject *error; /* Nouvelle erreur à rajouter */ -#define BINARY_FORMAT_ERRORS_ATTRIB PYTHON_GET_DEF_FULL \ +#define PROGRAM_FORMAT_ERRORS_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - errors, py_binary_format, \ + errors, py_program_format, \ "List of all detected errors which occurred while loading the binary.\n" \ "\n" \ "The result is a tuple of (pychrysalide.format.BinFormat.BinaryFormatError," \ @@ -1028,24 +1147,24 @@ static PyObject *py_binary_format_get_errors(PyObject *self, void *closure) " description for each error." \ ) - format = G_BIN_FORMAT(pygobject_get(self)); + format = G_PROGRAM_FORMAT(pygobject_get(self)); - g_binary_format_lock_errors(format); + g_program_format_lock_errors(format); - count = g_binary_format_count_errors(format); + count = g_program_format_count_errors(format); result = PyTuple_New(count); for (i = 0; i < count; i++) { #ifndef NDEBUG - status = g_binary_format_get_error(format, i, &type, &addr, &desc); + status = g_program_format_get_error(format, i, &type, &addr, &desc); assert(status); #else - g_binary_format_get_error(format, i, &type, &addr, &desc); + g_program_format_get_error(format, i, &type, &addr, &desc); #endif - py_type = cast_with_constants_group_from_type(get_python_binary_format_type(), "BinaryFormatError", type); + py_type = cast_with_constants_group_from_type(get_python_program_format_type(), "BinaryFormatError", type); error = Py_BuildValue("OO&s", py_type, build_from_internal_vmpa, &addr, desc); Py_DECREF(py_type); @@ -1053,11 +1172,12 @@ static PyObject *py_binary_format_get_errors(PyObject *self, void *closure) } - g_binary_format_unlock_errors(format); + g_program_format_unlock_errors(format); return result; } +#endif /****************************************************************************** @@ -1072,51 +1192,56 @@ static PyObject *py_binary_format_get_errors(PyObject *self, void *closure) * * ******************************************************************************/ -PyTypeObject *get_python_binary_format_type(void) +PyTypeObject *get_python_program_format_type(void) { - static PyMethodDef py_bin_format_methods[] = { - BINARY_FORMAT_SET_FLAG_METHOD, - BINARY_FORMAT_UNSET_FLAG_METHOD, - BINARY_FORMAT_HAS_FLAG_METHOD, - BINARY_FORMAT_REGISTER_CODE_POINT_METHOD, - BINARY_FORMAT_ADD_SYMBOL_METHOD, - BINARY_FORMAT_REMOVE_SYMBOL_METHOD, - BINARY_FORMAT_FIND_SYMBOL_BY_LABEL_METHOD, - BINARY_FORMAT_FIND_SYMBOL_AT_METHOD, - BINARY_FORMAT_FIND_NEXT_SYMBOL_AT_METHOD, - BINARY_FORMAT_RESOLVE_SYMBOL_METHOD, - BINARY_FORMAT_ADD_ERROR_METHOD, + static PyMethodDef py_program_format_methods[] = { + PROGRAM_FORMAT_GET_ENDIANNESS_WRAPPER, + PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER, + PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD, + /* + PROGRAM_FORMAT_SET_FLAG_METHOD, + PROGRAM_FORMAT_UNSET_FLAG_METHOD, + PROGRAM_FORMAT_HAS_FLAG_METHOD, + PROGRAM_FORMAT_REGISTER_CODE_POINT_METHOD, + PROGRAM_FORMAT_ADD_SYMBOL_METHOD, + PROGRAM_FORMAT_REMOVE_SYMBOL_METHOD, + PROGRAM_FORMAT_FIND_SYMBOL_BY_LABEL_METHOD, + PROGRAM_FORMAT_FIND_SYMBOL_AT_METHOD, + PROGRAM_FORMAT_FIND_NEXT_SYMBOL_AT_METHOD, + PROGRAM_FORMAT_RESOLVE_SYMBOL_METHOD, + PROGRAM_FORMAT_ADD_ERROR_METHOD, + */ { NULL } }; - static PyGetSetDef py_bin_format_getseters[] = { - BINARY_FORMAT_FLAGS_ATTRIB, - BINARY_FORMAT_ENDIANNESS_ATTRIB, - BINARY_FORMAT_SYMBOLS_ATTRIB, - BINARY_FORMAT_ERRORS_ATTRIB, + static PyGetSetDef py_program_format_getseters[] = { + //PROGRAM_FORMAT_FLAGS_ATTRIB, + PROGRAM_FORMAT_ENDIANNESS_ATTRIB, + //PROGRAM_FORMAT_SYMBOLS_ATTRIB, + //PROGRAM_FORMAT_ERRORS_ATTRIB, { NULL } }; - static PyTypeObject py_bin_format_type = { + static PyTypeObject py_program_format_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.format.BinFormat", + .tp_name = "pychrysalide.format.ProgramFormat", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_BASETYPE, - .tp_doc = BINARY_FORMAT_DOC, + .tp_doc = PROGRAM_FORMAT_DOC, - .tp_methods = py_bin_format_methods, - .tp_getset = py_bin_format_getseters, + .tp_methods = py_program_format_methods, + .tp_getset = py_program_format_getseters, - .tp_init = py_binary_format_init, - .tp_new = py_binary_format_new, + .tp_init = py_program_format_init, + .tp_new = py_program_format_new, }; - return &py_bin_format_type; + return &py_program_format_type; } @@ -1133,13 +1258,13 @@ PyTypeObject *get_python_binary_format_type(void) * * ******************************************************************************/ -bool ensure_python_binary_format_is_registered(void) +bool ensure_python_program_format_is_registered(void) { PyTypeObject *type; /* Type Python 'BinFormat' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ - type = get_python_binary_format_type(); + type = get_python_program_format_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -1150,11 +1275,15 @@ bool ensure_python_binary_format_is_registered(void) if (!ensure_python_known_format_is_registered()) return false; - if (!register_class_for_pygobject(dict, G_TYPE_BIN_FORMAT, type)) + pyg_register_class_init(G_TYPE_PROGRAM_FORMAT, (PyGClassInitFunc)py_program_format_init_gclass); + + if (!register_class_for_pygobject(dict, G_TYPE_PROGRAM_FORMAT, type)) return false; - if (!define_binary_format_constants(type)) + /* + if (!define_program_format_constants(type)) return false; + */ } @@ -1176,11 +1305,11 @@ bool ensure_python_binary_format_is_registered(void) * * ******************************************************************************/ -int convert_to_binary_format(PyObject *arg, void *dst) +int convert_to_program_format(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_binary_format_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_program_format_type()); switch (result) { @@ -1194,7 +1323,7 @@ int convert_to_binary_format(PyObject *arg, void *dst) break; case 1: - *((GBinFormat **)dst) = G_BIN_FORMAT(pygobject_get(arg)); + *((GProgramFormat **)dst) = G_PROGRAM_FORMAT(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/format/program.h b/plugins/pychrysalide/format/program.h new file mode 100644 index 0000000..647d122 --- /dev/null +++ b/plugins/pychrysalide/format/program.h @@ -0,0 +1,48 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * program.h - prototypes pour l'équivalent Python du fichier "format/program.h" + * + * Copyright (C) 2018-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_FORMAT_PROGRAM_H +#define _PLUGINS_PYCHRYSALIDE_FORMAT_PROGRAM_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* ---------------------------- FORMAT BINAIRE GENERIQUE ---------------------------- */ + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_program_format_type(void); + +/* Prend en charge l'objet 'pychrysalide.format.ProgramFormat'. */ +bool ensure_python_program_format_is_registered(void); + +/* Tente de convertir en format de binaire. */ +int convert_to_program_format(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_FORMAT_PROGRAM_H */ diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am index 2ed2aa5..6d3d746 100644 --- a/plugins/pychrysalide/glibext/Makefile.am +++ b/plugins/pychrysalide/glibext/Makefile.am @@ -1,32 +1,50 @@ -noinst_LTLIBRARIES = libpychrysaglibext.la +noinst_LTLIBRARIES = libpychrysaglibext.la libpychrysaglibextui.la + +# libpychrysaglibext_la_SOURCES = \ +# binarycursor.h binarycursor.c \ +# buffercache.h buffercache.c \ +# configuration.h configuration.c \ +# linecursor.h linecursor.c + +# if BUILD_GTK_SUPPORT + +# libpychrysaglibext_la_SOURCES += \ +# bufferview.h bufferview.c \ +# loadedpanel.h loadedpanel.c \ +# named.h named.c + +# endif libpychrysaglibext_la_SOURCES = \ + comparable.h comparable.c \ constants.h constants.c \ - binarycursor.h binarycursor.c \ - binportion.h binportion.c \ - buffercache.h buffercache.c \ - bufferline.h bufferline.c \ - comparison.h comparison.c \ - configuration.h configuration.c \ - linecursor.h linecursor.c \ - linegen.h linegen.c \ + hashable.h hashable.c \ module.h module.c \ - singleton.h singleton.c - -if BUILD_GTK_SUPPORT + objhole.h objhole.c \ + portion.h portion.c \ + secstorage.h secstorage.c \ + serialize.h serialize.c \ + singleton.h singleton.c \ + storage.h storage.c \ + strbuilder.h strbuilder.c \ + work.h work.c \ + workqueue.h workqueue.c + +libpychrysaglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT -libpychrysaglibext_la_SOURCES += \ - bufferview.h bufferview.c \ - loadedpanel.h loadedpanel.c \ - named.h named.c -endif +libpychrysaglibextui_la_SOURCES = \ + bufferline.h bufferline.c \ + constants-ui.h constants-ui.c \ + generator.h generator.c \ + module-ui.h module-ui.c -libpychrysaglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpychrysaglibextui_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT devdir = $(includedir)/chrysalide/$(subdir) -dev_HEADERS = $(libpychrysaglibext_la_SOURCES:%c=) +dev_HEADERS = $(libpychrysaglibext_la_SOURCES:%c=) $(libpychrysaglibextui_la_SOURCES:%c=) diff --git a/plugins/pychrysalide/glibext/bufferline.c b/plugins/pychrysalide/glibext/bufferline.c index 09404bc..c5b3664 100644 --- a/plugins/pychrysalide/glibext/bufferline.c +++ b/plugins/pychrysalide/glibext/bufferline.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * bufferline.c - équivalent Python du fichier "glibext/bufferline.h" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,25 +26,38 @@ #include <assert.h> -#include <malloc.h> +//#include <malloc.h> #include <pygobject.h> -#include <i18n.h> -#include <glibext/bufferline.h> -#include <plugins/dt.h> +#include <glibext/bufferline-int.h> -#include "constants.h" +//#include "constants.h" #include "../access.h" #include "../helpers.h" -#include "../arch/vmpa.h" +//#include "../arch/vmpa.h" +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(buffer_line, G_TYPE_BUFFER_LINE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_buffer_line_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ + + /* Accompagne la création d'une instance dérivée en Python. */ static PyObject *py_buffer_line_new(PyTypeObject *, PyObject *, PyObject *); +#if 0 + /* Ajoute du texte à formater dans une ligne donnée. */ static PyObject *py_buffer_line_append_text(PyObject *, PyObject *); @@ -54,29 +67,34 @@ static PyObject *py_buffer_line_get_text(PyObject *, PyObject *); /* Renseigne sur les propriétés particulières liées à une ligne. */ static PyObject *py_buffer_line_get_flags(PyObject *, void *); +#endif + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Accompagne la création d'une instance dérivée en Python. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_buffer_line_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ + unsigned char col_count; /* Qté de colonnes attendues */ + int ret; /* Bilan de lecture des args. */ + GBufferLine *line; /* Ligne en version native */ #define BUFFER_LINE_DOC \ "The BufferLine object is used to display processed data: disassembled" \ @@ -84,51 +102,40 @@ static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " BufferLine()" \ + " BufferLine(col_count)" \ + "\n" \ + " Where *col_count* is a integer value providing the expected number of"\ + " rendering columns." \ "\n" \ "Such objets aim to be created from the Chrysalide core only, and" \ " then get populated on demand. Thus, these lines can be viewed as" \ " cached lines and their properties have to be set through the" \ " pychrysalide.glibext.BufferCache instance which contains them." - /* Validations diverses */ - - base = get_python_buffer_line_type(); + /* Récupération des paramètres */ - if (type == base) - goto simple_way; + ret = PyArg_ParseTuple(args, "B", &col_count); + if (!ret) return -1; - /* Mise en place d'un type dédié */ + /* Initialisation d'un objet GLib */ - first_time = (g_type_from_name(type->tp_name) == 0); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - gtype = build_dynamic_type(G_TYPE_BUFFER_LINE, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); + /* Eléments de base */ - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); + line = G_BUFFER_LINE(pygobject_get(self)); - exit: + if (!g_buffer_line_create(line, col_count)) + return -1; - return result; + return 0; } +#if 0 + /****************************************************************************** * * * Paramètres : self = classe représentant une ligne de tampon. * @@ -256,6 +263,7 @@ static PyObject *py_buffer_line_get_flags(PyObject *self, void *closure) return result; } +#endif /****************************************************************************** @@ -273,20 +281,24 @@ static PyObject *py_buffer_line_get_flags(PyObject *self, void *closure) PyTypeObject *get_python_buffer_line_type(void) { static PyMethodDef py_buffer_line_methods[] = { + /* BUFFER_LINE_APPEND_TEXT_METHOD, { "get_text", py_buffer_line_get_text, METH_VARARGS, "get_text($self, first_col, last_col, markup, /)\n--\n\nProvide the text of a buffer line." }, + */ { NULL } }; static PyGetSetDef py_buffer_line_getseters[] = { + /* { "flags", py_buffer_line_get_flags, NULL, "Current flags of the buffer line.", NULL }, + */ { NULL } }; @@ -304,7 +316,8 @@ PyTypeObject *get_python_buffer_line_type(void) .tp_methods = py_buffer_line_methods, .tp_getset = py_buffer_line_getseters, - .tp_new = py_buffer_line_new + .tp_init = py_buffer_line_init, + .tp_new = py_buffer_line_new, }; @@ -342,11 +355,13 @@ bool ensure_python_buffer_line_is_registered(void) if (!register_class_for_pygobject(dict, G_TYPE_BUFFER_LINE, type)) return false; + /* if (!define_line_segment_constants(type)) return false; if (!define_buffer_line_constants(type)) return false; + */ } diff --git a/plugins/pychrysalide/glibext/comparable.c b/plugins/pychrysalide/glibext/comparable.c new file mode 100644 index 0000000..e4982d7 --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.c @@ -0,0 +1,482 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - équivalent Python du fichier "glibext/comparable.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "comparable.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/comparable-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_comparable_object_interface_init(GComparableObjectInterface *, gpointer *); + +/* Réalise une comparaison étendue entre objets. */ +static int py_comparable_object_compare_wrapper(const GComparableObject *, const GComparableObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet le statut d'une comparaison effectuée par le parent. */ +static PyObject *py_comparable_object_parent_compare(PyObject *, PyObject *); + +/* Effectue une comparaison avec un objet 'ComparableObject'. */ +static PyObject *py_comparable_object_richcompare(PyObject *, PyObject *, int); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_comparable_object_interface_init(GComparableObjectInterface *iface, gpointer *unused) +{ +#define COMPARABLE_OBJECT_DOC \ + "The ComparableObject class provides an interface to compare" \ + " objects.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, ComparableObject):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.ComparableObject._compare().\n" + + iface->compare = py_comparable_object_compare_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_comparable_object_compare_wrapper(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define COMPARABLE_OBJECT_COMPARE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _compare, "$self, other", \ + METH_VARARGS, \ + "Abstract method allowing to compare two objects implementing" \ + " the interface. This method is used to handle rich comparisons"\ + " automatically.\n" \ + "\n" \ + "The result has to be an integer lesser than, equal to, or" \ + " greater than zero if *self* is found, respectively, to be" \ + " lesser than, to match, or to be greater than *other*.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); + + pyret = run_python_method(pyobj, "_compare", args); + + if (pyret != NULL) + { + if (PyLong_Check(pyret)) + result = PyLong_AsLong(pyret); + + else + PyErr_SetString(PyExc_TypeError, _("comparison status has to be a signed integer")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet le statut d'une comparaison effectuée par le parent.* +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_parent_compare(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + GComparableObject *other; /* Second objet à comparer */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *object; /* Mécanismes natifs */ + GComparableObjectInterface *iface; /* Interface utilisée */ + GComparableObjectInterface *parent_iface; /* Interface parente */ + int status; /* Bilan d'une comparaison */ + +#define COMPARABLE_OBJECT_PARENT_COMPARE_METHOD PYTHON_METHOD_DEF \ +( \ + parent_compare, "$sel, otherf", \ + METH_VARARGS, py_comparable_object, \ + "Provide the comparison status defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a signed integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.ComparableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._compare() function has to be" \ + " used instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + ret = PyArg_ParseTuple(args, "O&", convert_to_comparable_object, &other); + if (!ret) return NULL; + + object = G_COMPARABLE_OBJECT(pygobject_get(self)); + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the ComparableObject interface")); + + result = NULL; + + } + else + { + status = parent_iface->compare(object, other); + + result = PyLong_FromLong(status); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : a = premier object Python à consulter. * +* b = second object Python à consulter. * +* op = type de comparaison menée. * +* * +* Description : Effectue une comparaison avec un objet 'ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_richcompare(PyObject *a, PyObject *b, int op) +{ + PyObject *result; /* Bilan à retourner */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *obj_a; /* Instance à manipuler #1 */ + GComparableObject *obj_b; /* Instance à manipuler #2 */ + int status; /* Bilan d'une comparaison */ + + ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_object_type()); + if (!ret) + { + result = Py_NotImplemented; + goto cmp_done; + } + + obj_a = G_COMPARABLE_OBJECT(pygobject_get(a)); + obj_b = G_COMPARABLE_OBJECT(pygobject_get(b)); + + status = g_comparable_object_compare(obj_a, obj_b); + + switch (op) + { + case Py_LT: + result = (status < 0 ? Py_True : Py_False); + break; + + case Py_LE: + result = (status <= 0 ? Py_True : Py_False); + break; + + case Py_EQ: + result = (status == 0 ? Py_True : Py_False); + break; + + case Py_NE: + result = (status != 0 ? Py_True : Py_False); + break; + + case Py_GT: + result = (status > 0 ? Py_True : Py_False); + break; + + case Py_GE: + result = (status >= 0 ? Py_True : Py_False); + break; + + default: + assert(false); + result = Py_NotImplemented; + break; + + } + + cmp_done: + + Py_INCREF(result); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_comparable_object_type(void) +{ + static PyMethodDef py_comparable_object_methods[] = { + COMPARABLE_OBJECT_COMPARE_WRAPPER, + COMPARABLE_OBJECT_PARENT_COMPARE_METHOD, + { NULL } + }; + + static PyGetSetDef py_comparable_object_getseters[] = { + { NULL } + }; + + static PyTypeObject py_comparable_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ComparableObject", + .tp_basicsize = sizeof(PyObject), + + /** + * Une valeur de .tp_richcompare non nulle écarte la définition du + * champ .tp_hash à la valeur par défaut du type PyBaseObject_Type + * dans les préparatifs de la fonction Python inherit_slots(). + * + * Ces préparatifs se poursuivent avec type_ready_set_hash(), + * qui initialise .tp_hash avec PyObject_HashNotImplemented(), + * qui n'est donc pas un comportement par défaut. + * + * Côté PyGObject, la fonction pygobject_inherit_slots() y voit + * une implémentation de .tp_hash personnalisée, ce qui bloque + * la défintion d'autres personnalisations, comme celle de + * l'interface HashableObject. + * + * La valeur nominale nulle est ainsi écartée au préalable ici. + */ + .tp_hash = (hashfunc)_Py_HashPointer, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = COMPARABLE_OBJECT_DOC, + + .tp_richcompare = py_comparable_object_richcompare, + + .tp_methods = py_comparable_object_methods, + .tp_getset = py_comparable_object_getseters + + }; + + return &py_comparable_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.....ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_comparable_object_is_registered(void) +{ + PyTypeObject *type; /* Type 'ComparableObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_comparable_object_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_comparable_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_OBJECT, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'objet comparable. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_comparable_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable object"); + break; + + case 1: + *((GComparableObject **)dst) = G_COMPARABLE_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/comparable.h b/plugins/pychrysalide/glibext/comparable.h new file mode 100644 index 0000000..d4c6ecf --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour l'équivalent Python du fichier "glibext/comparable.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_comparable_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ComparableObject'. */ +bool ensure_python_comparable_object_is_registered(void); + +/* Tente de convertir en interface d'objet comparable. */ +int convert_to_comparable_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H */ diff --git a/plugins/pychrysalide/glibext/constants-ui.c b/plugins/pychrysalide/glibext/constants-ui.c new file mode 100644 index 0000000..4101600 --- /dev/null +++ b/plugins/pychrysalide/glibext/constants-ui.c @@ -0,0 +1,131 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * constants-ui.c - ajout des constantes pour les extensions graphique à la GLib + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "constants-ui.h" + + +#include <glibext/bufferline.h> + + +#include "../helpers.h" + + + +/****************************************************************************** +* * +* Paramètres : type = type dont le dictionnaire est à compléter. * +* * +* Description : Définit les constantes relatives aux lignes de tampon. * +* * +* Retour : true en cas de succès de l'opération, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool define_buffer_line_constants(PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + PyObject *values; /* Groupe de valeurs à établir */ + + values = PyDict_New(); + + result = add_const_to_group(values, "NONE", BLF_NONE); + if (result) result = add_const_to_group(values, "HAS_CODE", BLF_HAS_CODE); + if (result) result = add_const_to_group(values, "IS_LABEL", BLF_IS_LABEL); + if (result) result = add_const_to_group(values, "ENTRYPOINT", BLF_ENTRYPOINT); + if (result) result = add_const_to_group(values, "BOOKMARK", BLF_BOOKMARK); + if (result) result = add_const_to_group(values, "WIDTH_MANAGER", BLF_WIDTH_MANAGER); + if (result) result = add_const_to_group(values, "ALL", BLF_ALL); + + if (!result) + { + Py_DECREF(values); + goto exit; + } + + result = attach_constants_group_to_type(type, true, "BufferLineFlags", values, + "Optional flags linked to a rendering line."); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en constante BufferLineFlags. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_buffer_line_flags(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + unsigned long value; /* Valeur transcrite */ + + result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to BufferLineFlags"); + break; + + case 1: + value = PyLong_AsUnsignedLong(arg); + + if ((value & BLF_ALL) != value) + { + PyErr_SetString(PyExc_TypeError, "invalid value for BufferLineFlags"); + result = 0; + } + + else + *((BufferLineFlags *)dst) = value; + + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/constants-ui.h b/plugins/pychrysalide/glibext/constants-ui.h new file mode 100644 index 0000000..6c7bc6e --- /dev/null +++ b/plugins/pychrysalide/glibext/constants-ui.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * constants-ui.h - prototypes pour l'ajout des constantes pour les extensions graphique à la GLib + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H + + +#include <Python.h> +#include <stdbool.h> + + +/* Définit les constantes relatives aux lignes de tampon. */ +bool define_buffer_line_constants(PyTypeObject *); + +/* Tente de convertir en constante BufferLineFlags. */ +int convert_to_buffer_line_flags(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H */ diff --git a/plugins/pychrysalide/glibext/constants.c b/plugins/pychrysalide/glibext/constants.c index 169ffa2..f733cf6 100644 --- a/plugins/pychrysalide/glibext/constants.c +++ b/plugins/pychrysalide/glibext/constants.c @@ -25,16 +25,17 @@ #include "constants.h" +#include <glibext/portion.h> + +/* #include <i18n.h> -#include <glibext/bufferline.h> #include <glibext/comparison.h> #include <glibext/configuration.h> #include <glibext/linesegment.h> -#include <glibext/gbinportion.h> #ifdef INCLUDE_GTK_SUPPORT # include <glibext/gloadedpanel.h> #endif - +*/ #include "../helpers.h" @@ -58,6 +59,7 @@ bool define_binary_portion_constants(PyTypeObject *type) PyObject *strdict; /* Groupe de chaînes constantes*/ PyObject *values; /* Groupe de valeurs à établir */ + /* result = create_string_constants_group_to_type(type, "BinaryPortionCode", "Selector names for the CSS rendering.", &strdict); @@ -69,6 +71,7 @@ bool define_binary_portion_constants(PyTypeObject *type) if (!result) goto exit; + */ values = PyDict_New(); @@ -150,105 +153,7 @@ int convert_to_portion_access_rights(PyObject *arg, void *dst) } -/****************************************************************************** -* * -* Paramètres : type = type dont le dictionnaire est à compléter. * -* * -* Description : Définit les constantes relatives aux lignes de tampon. * -* * -* Retour : true en cas de succès de l'opération, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool define_buffer_line_constants(PyTypeObject *type) -{ - bool result; /* Bilan à retourner */ - PyObject *values; /* Groupe de valeurs à établir */ - - values = PyDict_New(); - - result = add_const_to_group(values, "NONE", BLF_NONE); - if (result) result = add_const_to_group(values, "HAS_CODE", BLF_HAS_CODE); - if (result) result = add_const_to_group(values, "IS_LABEL", BLF_IS_LABEL); - if (result) result = add_const_to_group(values, "ENTRYPOINT", BLF_ENTRYPOINT); - if (result) result = add_const_to_group(values, "BOOKMARK", BLF_BOOKMARK); - if (result) result = add_const_to_group(values, "WIDTH_MANAGER", BLF_WIDTH_MANAGER); - if (result) result = add_const_to_group(values, "ALL", BLF_ALL); - - if (!result) - { - Py_DECREF(values); - goto exit; - } - - result = attach_constants_group_to_type(type, true, "BufferLineFlags", values, - "Optional flags linked to a rendering line."); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : arg = argument quelconque à tenter de convertir. * -* dst = destination des valeurs récupérées en cas de succès. * -* * -* Description : Tente de convertir en constante BufferLineFlags. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_buffer_line_flags(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - unsigned long value; /* Valeur transcrite */ - - result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); - - switch (result) - { - case -1: - /* L'exception est déjà fixée par Python */ - result = 0; - break; - - case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to BufferLineFlags"); - break; - - case 1: - value = PyLong_AsUnsignedLong(arg); - - if ((value & BLF_ALL) != value) - { - PyErr_SetString(PyExc_TypeError, "invalid value for BufferLineFlags"); - result = 0; - } - - else - *((BufferLineFlags *)dst) = value; - - break; - - default: - assert(false); - break; - - } - - return result; - -} - - +#if 0 /****************************************************************************** * * * Paramètres : type = type dont le dictionnaire est à compléter. * @@ -617,3 +522,4 @@ int convert_to_scroll_position_tweak(PyObject *arg, void *dst) #endif +#endif diff --git a/plugins/pychrysalide/glibext/constants.h b/plugins/pychrysalide/glibext/constants.h index 4a4f6da..c695aa9 100644 --- a/plugins/pychrysalide/glibext/constants.h +++ b/plugins/pychrysalide/glibext/constants.h @@ -37,11 +37,7 @@ bool define_binary_portion_constants(PyTypeObject *); /* Tente de convertir en constante PortionAccessRights. */ int convert_to_portion_access_rights(PyObject *, void *); -/* Définit les constantes relatives aux lignes de tampon. */ -bool define_buffer_line_constants(PyTypeObject *); - -/* Tente de convertir en constante BufferLineFlags. */ -int convert_to_buffer_line_flags(PyObject *, void *); +#if 0 /* Définit les constantes relatives aux modes de comparaison. */ bool define_comparable_item_constants(PyTypeObject *); @@ -68,6 +64,8 @@ int convert_to_scroll_position_tweak(PyObject *, void *); #endif +#endif + #endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_H */ diff --git a/plugins/pychrysalide/glibext/linegen.c b/plugins/pychrysalide/glibext/generator.c index d7e96fd..3a9a8ab 100644 --- a/plugins/pychrysalide/glibext/linegen.c +++ b/plugins/pychrysalide/glibext/generator.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * linegen.c - équivalent Python du fichier "glibext/linegen.h" + * generator.c - équivalent Python du fichier "glibext/generator.h" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,21 +22,21 @@ */ -#include "linegen.h" +#include "generator.h" #include <pygobject.h> -#include <glibext/linegen-int.h> +#include <glibext/generator-int.h> #include "bufferline.h" -#include "constants.h" -#include "linecursor.h" +#include "constants-ui.h" +//#include "linecursor.h" #include "../access.h" #include "../helpers.h" -#include "../analysis/content.h" +//#include "../analysis/content.h" @@ -44,42 +44,55 @@ /* Procède à l'initialisation de l'interface de génération. */ -static void py_line_generator_interface_init(GLineGeneratorIface *, gpointer *); +static void py_token_generator_interface_init(GTokenGeneratorInterface *, gpointer *); /* Indique le nombre de ligne prêtes à être générées. */ -static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *); +static size_t py_token_generator_count_lines_wrapper(const GTokenGenerator *); + +/* Renseigne sur les propriétés liées à un générateur. */ +static BufferLineFlags py_token_generator_get_flags_wrapper(const GTokenGenerator *, size_t, size_t); + +/*Description : Etablit dans une ligne de rendu le contenu représenté. */ +static void py_token_generator_populate_line_wrappper(const GTokenGenerator *, size_t, size_t, GBufferLine *, void *); + + +#if 0 + /* Retrouve l'emplacement correspondant à une position donnée. */ -static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *, gint, size_t, size_t, GLineCursor **); +static void py_token_generator_compute_cursor_wrapper(const GTokenGenerator *, gint, size_t, size_t, GLineCursor **); /* Détermine si le conteneur s'inscrit dans une plage donnée. */ -static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *, size_t, size_t, const GLineCursor *); +static int py_token_generator_contain_cursor_wrapper(const GTokenGenerator *, size_t, size_t, const GLineCursor *); + + +#endif -/* Renseigne sur les propriétés liées à un générateur. */ -static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator *, size_t, size_t); -/* Imprime dans une ligne de rendu le contenu représenté. */ -static void py_line_generator_print_wrapper(GLineGenerator *, GBufferLine *, size_t, size_t, const GBinContent *); +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ -/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ +/* Renseigne sur les propriétés liées à un générateur. */ +static PyObject *py_token_generator_get_flags(PyObject *, PyObject *); + +/* Etablit dans une ligne de rendu le contenu représenté. */ +static PyObject *py_token_generator_populate_line(PyObject *self, PyObject *args); + +#if 0 /* Retrouve l'emplacement correspondant à une position donnée. */ -static PyObject *py_line_generator_compute_cursor(PyObject *, PyObject *); +static PyObject *py_token_generator_compute_cursor(PyObject *, PyObject *); /* Détermine si le conteneur s'inscrit dans une plage donnée. */ -static PyObject *py_line_generator_contain_cursor(PyObject *, PyObject *); +static PyObject *py_token_generator_contain_cursor(PyObject *, PyObject *); -/* Renseigne sur les propriétés liées à un générateur. */ -static PyObject *py_line_generator_get_flags(PyObject *, PyObject *); +#endif -/* Imprime dans une ligne de rendu le contenu représenté. */ -static PyObject *py_line_generator_print(PyObject *, PyObject *); /* Indique le nombre de ligne prêtes à être générées. */ -static PyObject *py_line_generator_get_lines_count(PyObject *, void *); +static PyObject *py_token_generator_get_lines_count(PyObject *, void *); @@ -101,31 +114,33 @@ static PyObject *py_line_generator_get_lines_count(PyObject *, void *); * * ******************************************************************************/ -static void py_line_generator_interface_init(GLineGeneratorIface *iface, gpointer *unused) +static void py_token_generator_interface_init(GTokenGeneratorInterface *iface, gpointer *unused) { -#define LINE_GENERATOR_DOC \ - "LineGenerator gives an interface to all objects which aim to produce" \ +#define TOKEN_GENERATOR_DOC \ + "TokenGenerator gives an interface to all objects which aim to produce" \ " content for rendering lines. Such lines can be exported to graphical" \ " interfaces or text files.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, LineGenerator):\n" \ + " class NewImplem(GObject.Object, TokenGenerator):\n" \ " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ - "* pychrysalide.glibext.LineGenerator._count_lines();\n" \ - "* pychrysalide.glibext.LineGenerator._compute_cursor();\n" \ - "* pychrysalide.glibext.LineGenerator._contain_cursor();\n" \ - "* pychrysalide.glibext.LineGenerator._get_flags();\n" \ - "* pychrysalide.glibext.LineGenerator._print();\n" \ - - iface->count = py_line_generator_count_lines_wrapper; - iface->compute = py_line_generator_compute_cursor_wrapper; - iface->contain = py_line_generator_contain_cursor_wrapper; - iface->get_flags = py_line_generator_get_flags_wrapper; - iface->print = py_line_generator_print_wrapper; + "* pychrysalide.glibext.TokenGenerator._count_lines();\n" \ + "* pychrysalide.glibext.TokenGenerator._get_flags();\n" \ + "* pychrysalide.glibext.TokenGenerator._populate_line();\n" \ + "* pychrysalide.glibext.TokenGenerator._compute_cursor();\n" \ + "* pychrysalide.glibext.TokenGenerator._contain_cursor().\n" + + iface->count = py_token_generator_count_lines_wrapper; + iface->get_flags = py_token_generator_get_flags_wrapper; + iface->populate = py_token_generator_populate_line_wrappper; +#if 0 + iface->compute = py_token_generator_compute_cursor_wrapper; + iface->contain = py_token_generator_contain_cursor_wrapper; +#endif } @@ -142,7 +157,7 @@ static void py_line_generator_interface_init(GLineGeneratorIface *iface, gpointe * * ******************************************************************************/ -static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *generator) +static size_t py_token_generator_count_lines_wrapper(const GTokenGenerator *generator) { size_t result; /* Décompte à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ @@ -150,7 +165,7 @@ static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *genera PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_COUNT_LINES_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_COUNT_LINES_WRAPPER PYTHON_WRAPPER_DEF \ ( \ _count_lines, "$self, /", \ METH_NOARGS, \ @@ -193,61 +208,61 @@ static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *genera /****************************************************************************** * * * Paramètres : generator = générateur à consulter. * -* x = position géographique sur la ligne concernée. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * * * -* Description : Retrouve l'emplacement correspondant à une position donnée. * +* Description : Renseigne sur les propriétés liées à un générateur. * * * -* Retour : Emplacement constitué. * +* Retour : Propriétés particulières associées. * * * * Remarques : - * * * ******************************************************************************/ -static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *generator, gint x, size_t index, size_t repeat, GLineCursor **cursor) +static BufferLineFlags py_token_generator_get_flags_wrapper(const GTokenGenerator *generator, size_t index, size_t repeat) { + BufferLineFlags result; /* Fanions à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_COMPUTE_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_GET_FLAGS_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _compute_cursor, "$self, x, index, repeat, /", \ + _get_flags, "$self, index, repeat, /", \ METH_VARARGS, \ - "Abstract method used to create a new cursor for a given" \ - " location inside displayed lines.\n" \ + "Abstract method used to provide flags for a given rendering" \ + " line.\n" \ "\n" \ - "The position on the horizontal axis, the line index and the" \ - " number of repetitions (only relevant if the generator" \ - " produces several lines) give indications about the active" \ - " position.\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position.\n" \ "\n" \ - "The result has to be a pychrysalide.glibext.LineCursor" \ - " instance." \ + "The result has to be a" \ + " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n" \ ) + result = BLF_NONE; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_compute_cursor")) + if (has_python_method(pyobj, "_get_flags")) { - args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyLong_FromSize_t(x)); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); - pyret = run_python_method(pyobj, "_compute_cursor", args); + pyret = run_python_method(pyobj, "_get_flags", args); if (pyret != NULL) { - ret = convert_to_line_cursor(pyret, cursor); + ret = convert_to_buffer_line_flags(pyret, &result); if (ret != 1) - *cursor = NULL; + result = BLF_NONE; Py_DECREF(pyret); @@ -261,75 +276,66 @@ static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *gener PyGILState_Release(gstate); + return result; + } /****************************************************************************** * * -* Paramètres : generator = générateur à consulter. * +* Paramètres : generator = générateur à utiliser pour l'impression. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * -* cursor = emplacement à analyser. * +* line = ligne de rendu à compléter. * +* data = éventuelle donnée complémentaire fournie. * * * -* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * +* Description : Etablit dans une ligne de rendu le contenu représenté. * * * -* Retour : Bilan de la détermination, utilisable en comparaisons. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *generator, size_t index, size_t repeat, const GLineCursor *cursor) +static void py_token_generator_populate_line_wrappper(const GTokenGenerator *generator, size_t index, size_t repeat, GBufferLine *line, void *data) { - int result; /* Bilan d'analyse à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ - int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_CONTAIN_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_POPULATE_LINE_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _contain_cursor, "$self, index, repeat, cursor, /", \ + _populate_line, "$self, index, repeat, line, /", \ METH_VARARGS, \ - "Abstract method used to check the position of a cursor in" \ - " relation to rendering lines.\n" \ + "Abstract method used to generate content into a rendering" \ + " line.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ + "This rendering output is pointed by the *line* argument, which"\ + " is a provided pychrysalide.glibext.BufferLine instance. The" \ + " line *index* and the number of repetitions (only relevant" \ " if the generator produces several lines) give indications" \ - " about the active position. The cursor is a" \ - " pychrysalide.glibext.LineCursor instance.\n" \ + " about the current rendering position.\n" \ "\n" \ - "The result has to be an integer less than, equal to, or" \ - " greater than zero if the cursor is, respectively, before," \ - " inside or after the area covered by the generator." \ + "If set, the content is a pychrysalide.analysis.BinContent" \ + " instance providing access to the processed binary data." \ ) - result = 0; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_contain_cursor")) + if (has_python_method(pyobj, "_populate_line")) { args = PyTuple_New(3); PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); - PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(cursor))); + PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(line))); + //PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(content))); - pyret = run_python_method(pyobj, "_contain_cursor", args); - - if (pyret != NULL) - { - ret = PyLong_Check(pyret); + pyret = run_python_method(pyobj, "_populate_line", args); - if (ret) - result = PyLong_AsLong(pyret); - - Py_DECREF(pyret); - - } + Py_XDECREF(pyret); Py_DECREF(args); @@ -339,69 +345,70 @@ static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *genera PyGILState_Release(gstate); - return result; - } +#if 0 + + /****************************************************************************** * * * Paramètres : generator = générateur à consulter. * +* x = position géographique sur la ligne concernée. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * * * -* Description : Renseigne sur les propriétés liées à un générateur. * +* Description : Retrouve l'emplacement correspondant à une position donnée. * * * -* Retour : Propriétés particulières associées. * +* Retour : Emplacement constitué. * * * * Remarques : - * * * ******************************************************************************/ -static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator *generator, size_t index, size_t repeat) +static void py_token_generator_compute_cursor_wrapper(const GTokenGenerator *generator, gint x, size_t index, size_t repeat, GLineCursor **cursor) { - BufferLineFlags result; /* Fanions à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_GET_FLAGS_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_COMPUTE_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _get_flags, "$self, index, repeat, /", \ + _compute_cursor, "$self, x, index, repeat, /", \ METH_VARARGS, \ - "Abstract method used to provide flags for a given rendering" \ - " line.\n" \ + "Abstract method used to create a new cursor for a given" \ + " location inside displayed lines.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position.\n" \ + "The position on the horizontal axis, the line index and the" \ + " number of repetitions (only relevant if the generator" \ + " produces several lines) give indications about the active" \ + " position.\n" \ "\n" \ - "The result has to be a" \ - " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n" \ + "The result has to be a pychrysalide.glibext.LineCursor" \ + " instance." \ ) - result = BLF_NONE; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_get_flags")) + if (has_python_method(pyobj, "_compute_cursor")) { - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); + args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(x)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); - pyret = run_python_method(pyobj, "_get_flags", args); + pyret = run_python_method(pyobj, "_compute_cursor", args); if (pyret != NULL) { - ret = convert_to_buffer_line_flags(pyret, &result); + ret = convert_to_line_cursor(pyret, cursor); if (ret != 1) - result = BLF_NONE; + *cursor = NULL; Py_DECREF(pyret); @@ -415,65 +422,75 @@ static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator PyGILState_Release(gstate); - return result; - } /****************************************************************************** * * -* Paramètres : generator = générateur à utiliser pour l'impression. * -* line = ligne de rendu à compléter. * +* Paramètres : generator = générateur à consulter. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * -* content = éventuel contenu binaire brut à imprimer. * +* cursor = emplacement à analyser. * * * -* Description : Imprime dans une ligne de rendu le contenu représenté. * +* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * * * -* Retour : - * +* Retour : Bilan de la détermination, utilisable en comparaisons. * * * * Remarques : - * * * ******************************************************************************/ -static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content) +static int py_token_generator_contain_cursor_wrapper(const GTokenGenerator *generator, size_t index, size_t repeat, const GLineCursor *cursor) { + int result; /* Bilan d'analyse à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ + int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_CONTAIN_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _print, "$self, line, index, repeat, content, /", \ + _contain_cursor, "$self, index, repeat, cursor, /", \ METH_VARARGS, \ - "Abstract method used to generate content into a rendering" \ - " line, which is a provided pychrysalide.glibext.BufferLine" \ - " instance.\n" \ + "Abstract method used to check the position of a cursor in" \ + " relation to rendering lines.\n" \ "\n" \ "The line index and the number of repetitions (only relevant" \ " if the generator produces several lines) give indications" \ - " about the current rendering position.\n" \ + " about the active position. The cursor is a" \ + " pychrysalide.glibext.LineCursor instance.\n" \ "\n" \ - "If set, the content is a pychrysalide.analysis.BinContent" \ - " instance providing access to the processed binary data." \ + "The result has to be an integer less than, equal to, or" \ + " greater than zero if the cursor is, respectively, before," \ + " inside or after the area covered by the generator." \ ) + result = 0; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_print")) + if (has_python_method(pyobj, "_contain_cursor")) { - args = PyTuple_New(4); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); - PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(content))); + args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); + PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(cursor))); - pyret = run_python_method(pyobj, "_print", args); + pyret = run_python_method(pyobj, "_contain_cursor", args); - Py_XDECREF(pyret); + if (pyret != NULL) + { + ret = PyLong_Check(pyret); + + if (ret) + result = PyLong_AsLong(pyret); + + Py_DECREF(pyret); + + } Py_DECREF(args); @@ -483,12 +500,16 @@ static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLi PyGILState_Release(gstate); + return result; + } +#endif + /* ---------------------------------------------------------------------------------- */ -/* CONNEXION AVEC L'API DE PYTHON */ +/* LIAISON DE FONCTIONNALITES AVEC L'API PYTHON */ /* ---------------------------------------------------------------------------------- */ @@ -497,57 +518,45 @@ static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLi * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Retrouve l'emplacement correspondant à une position donnée. * +* Description : Renseigne sur les propriétés liées à un générateur. * * * -* Retour : Emplacement constitué. * +* Retour : Propriétés particulières associées. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_compute_cursor(PyObject *self, PyObject *args) +static PyObject *py_token_generator_get_flags(PyObject *self, PyObject *args) { PyObject *result; /* Propriétés à retourner */ - int x; /* Position horizontale */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - GLineCursor *cursor; /* Curseur nouveau obtenu */ + GTokenGenerator *generator; /* Version native */ + BufferLineFlags flags; /* Propriétés courantes */ -#define LINE_GENERATOR_COMPUTE_CURSOR_METHOD PYTHON_METHOD_DEF \ -( \ - compute_cursor, "$self, x, index, repeat, /", \ - METH_VARARGS, py_line_generator, \ - "Create a a new cursor for a given location inside displayed" \ - " lines.\n" \ - "\n" \ - "The position on the horizontal axis, the line index and the" \ - " number of repetitions (only relevant if the generator" \ - " produces several lines) give indications about the active" \ - " position.\n" \ - "\n" \ - "The result has to be a pychrysalide.glibext.LineCursor" \ - " instance." \ +#define TOKEN_GENERATOR_GET_FLAGS_METHOD PYTHON_METHOD_DEF \ +( \ + get_flags, "$self, index, repeat, /", \ + METH_VARARGS, py_token_generator, \ + "Get the flags of a given position from the generator.\n" \ + "\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position.\n" \ + "\n" \ + "The result is a pychrysalide.glibext.BufferLine.BufferLineFlags" \ + " value." \ ) - ret = PyArg_ParseTuple(args, "inn", &x, &index, &repeat); + ret = PyArg_ParseTuple(args, "nn", &index, &repeat); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - cursor = g_line_generator_compute_cursor(generator, x, index, repeat); + flags = g_token_generator_get_flags(generator, index, repeat); - if (cursor != NULL) - { - result = pygobject_new(G_OBJECT(cursor)); - g_object_unref(G_OBJECT(cursor)); - } - else - { - result = Py_None; - Py_INCREF(result); - } + result = cast_with_constants_group_from_type(get_python_buffer_line_type(), "BufferLineFlags", flags); return result; @@ -559,99 +568,108 @@ static PyObject *py_line_generator_compute_cursor(PyObject *self, PyObject *args * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * +* Description : Etablit dans une ligne de rendu le contenu représenté. * * * -* Retour : Bilan de la détermination, utilisable en comparaisons. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_contain_cursor(PyObject *self, PyObject *args) +static PyObject *py_token_generator_populate_line(PyObject *self, PyObject *args) { - PyObject *result; /* Propriétés à retourner */ + GBufferLine *line; /* Ligne de rendu à compléter */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ - GLineCursor *cursor; /* Curseur à venir situer */ + GTokenGenerator *generator; /* Version native */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - int status; /* Bilan d'une analyse */ -#define LINE_GENERATOR_CONTAIN_CURSOR_METHOD PYTHON_METHOD_DEF \ +#define TOKEN_GENERATOR_POPULATE_LINE_METHOD PYTHON_METHOD_DEF \ ( \ - contain_cursor, "$self, index, repeat, cursor, /", \ - METH_VARARGS, py_line_generator, \ - "Check the position of a cursor in relation to rendering" \ - " lines.\n" \ + populate_line, "$self, index, repeat, line, /", \ + METH_VARARGS, py_token_generator, \ + "Produce output into a rendering line with optional content.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position. The cursor is a" \ - " pychrysalide.glibext.LineCursor instance.\n" \ + "The provided *line* is a pychrysalide.glibext.BufferLine" \ + " instance. The *index* and the number of repetitions (only" \ + " relevant if the generator produces several lines) give" \ + " indications about the current rendering position.\n" \ "\n" \ - "The result has to be an integer less than, equal to, or" \ - " greater than zero if the cursor is, respectively, before," \ - " inside or after the area covered by the generator." \ + "If set, the content is a pychrysalide.analysis.BinContent" \ + " instance providing access to the processed binary data." \ ) - ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_line_cursor, &cursor); + ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_buffer_line, &line); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - status = g_line_generator_contain_cursor(generator, index, repeat, cursor); + g_token_generator_populate_line(generator, index, repeat, line, NULL); - result = PyLong_FromLong(status); - - return result; + Py_RETURN_NONE; } + +#if 0 + /****************************************************************************** * * * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Renseigne sur les propriétés liées à un générateur. * +* Description : Retrouve l'emplacement correspondant à une position donnée. * * * -* Retour : Propriétés particulières associées. * +* Retour : Emplacement constitué. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_get_flags(PyObject *self, PyObject *args) +static PyObject *py_token_generator_compute_cursor(PyObject *self, PyObject *args) { PyObject *result; /* Propriétés à retourner */ + int x; /* Position horizontale */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - BufferLineFlags flags; /* Propriétés courantes */ + GTokenGenerator *generator; /* Version native */ + GLineCursor *cursor; /* Curseur nouveau obtenu */ -#define LINE_GENERATOR_GET_FLAGS_METHOD PYTHON_METHOD_DEF \ -( \ - get_flags, "$self, index, repeat, /", \ - METH_VARARGS, py_line_generator, \ - "Get the flags of a given position from the generator.\n" \ - "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position.\n" \ - "\n" \ - "The result is a pychrysalide.glibext.BufferLine.BufferLineFlags" \ - " value." \ +#define TOKEN_GENERATOR_COMPUTE_CURSOR_METHOD PYTHON_METHOD_DEF \ +( \ + compute_cursor, "$self, x, index, repeat, /", \ + METH_VARARGS, py_token_generator, \ + "Create a a new cursor for a given location inside displayed" \ + " lines.\n" \ + "\n" \ + "The position on the horizontal axis, the line index and the" \ + " number of repetitions (only relevant if the generator" \ + " produces several lines) give indications about the active" \ + " position.\n" \ + "\n" \ + "The result has to be a pychrysalide.glibext.LineCursor" \ + " instance." \ ) - ret = PyArg_ParseTuple(args, "nn", &index, &repeat); + ret = PyArg_ParseTuple(args, "inn", &x, &index, &repeat); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - flags = g_line_generator_get_flags(generator, index, repeat); + cursor = g_token_generator_compute_cursor(generator, x, index, repeat); - result = cast_with_constants_group_from_type(get_python_buffer_line_type(), "BufferLineFlags", flags); + if (cursor != NULL) + { + result = pygobject_new(G_OBJECT(cursor)); + g_object_unref(G_OBJECT(cursor)); + } + else + { + result = Py_None; + Py_INCREF(result); + } return result; @@ -663,50 +681,56 @@ static PyObject *py_line_generator_get_flags(PyObject *self, PyObject *args) * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Imprime dans une ligne de rendu le contenu représenté. * +* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * * * -* Retour : - * +* Retour : Bilan de la détermination, utilisable en comparaisons. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_print(PyObject *self, PyObject *args) +static PyObject *py_token_generator_contain_cursor(PyObject *self, PyObject *args) { - GBufferLine *line; /* Ligne de rendu à compléter */ + PyObject *result; /* Propriétés à retourner */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ - GBinContent *content; /* Contenu binaire associé */ - GLineGenerator *generator; /* Version native */ + GLineCursor *cursor; /* Curseur à venir situer */ int ret; /* Bilan de lecture des args. */ + GTokenGenerator *generator; /* Version native */ + int status; /* Bilan d'une analyse */ -#define LINE_GENERATOR_PRINT_METHOD PYTHON_METHOD_DEF \ +#define TOKEN_GENERATOR_CONTAIN_CURSOR_METHOD PYTHON_METHOD_DEF \ ( \ - print, "$self, line, index, repeat, content, /", \ - METH_VARARGS, py_line_generator, \ - "Produce output into a rendering line with optional content.\n" \ + contain_cursor, "$self, index, repeat, cursor, /", \ + METH_VARARGS, py_token_generator, \ + "Check the position of a cursor in relation to rendering" \ + " lines.\n" \ "\n" \ - "The provided line is a pychrysalide.glibext.BufferLine" \ - " instance. The index and the number of repetitions (only" \ - " relevant if the generator produces several lines) give" \ - " indications about the current rendering position.\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position. The cursor is a" \ + " pychrysalide.glibext.LineCursor instance.\n" \ "\n" \ - "If set, the content is a pychrysalide.analysis.BinContent" \ - " instance providing access to the processed binary data." \ + "The result has to be an integer less than, equal to, or" \ + " greater than zero if the cursor is, respectively, before," \ + " inside or after the area covered by the generator." \ ) - ret = PyArg_ParseTuple(args, "O&nnO&", convert_to_buffer_line, &line, &index, - &repeat, convert_to_binary_content, &content); + ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_line_cursor, &cursor); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - g_line_generator_print(generator, line, index, repeat, content); + status = g_token_generator_contain_cursor(generator, index, repeat, cursor); - Py_RETURN_NONE; + result = PyLong_FromLong(status); + + return result; } +#endif + /****************************************************************************** * * @@ -721,24 +745,24 @@ static PyObject *py_line_generator_print(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_line_generator_get_lines_count(PyObject *self, void *closure) +static PyObject *py_token_generator_get_lines_count(PyObject *self, void *closure) { PyObject *result; /* Décompte à retourner */ - GLineGenerator *generator; /* Version native */ + GTokenGenerator *generator; /* Version native */ size_t count; /* Nombre de lignes présentes */ -#define LINE_GENERATOR_LINES_COUNT_ATTRIB PYTHON_GET_DEF_FULL \ +#define TOKEN_GENERATOR_LINES_COUNT_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - lines_count, py_line_generator, \ + lines_count, py_token_generator, \ "Quantity of lines produced by the generator.\n" \ "\n" \ "This number may vary between calls, if a width has changed" \ " for instance." \ ) - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - count = g_line_generator_count_lines(generator); + count = g_token_generator_count_lines(generator); result = PyLong_FromSize_t(count); @@ -759,43 +783,47 @@ static PyObject *py_line_generator_get_lines_count(PyObject *self, void *closure * * ******************************************************************************/ -PyTypeObject *get_python_line_generator_type(void) +PyTypeObject *get_python_token_generator_type(void) { - static PyMethodDef py_line_generator_methods[] = { - LINE_GENERATOR_COUNT_LINES_WRAPPER, - LINE_GENERATOR_COMPUTE_CURSOR_WRAPPER, - LINE_GENERATOR_CONTAIN_CURSOR_WRAPPER, - LINE_GENERATOR_GET_FLAGS_WRAPPER, - LINE_GENERATOR_PRINT_WRAPPER, - LINE_GENERATOR_COMPUTE_CURSOR_METHOD, - LINE_GENERATOR_CONTAIN_CURSOR_METHOD, - LINE_GENERATOR_GET_FLAGS_METHOD, - LINE_GENERATOR_PRINT_METHOD, + static PyMethodDef py_token_generator_methods[] = { + TOKEN_GENERATOR_COUNT_LINES_WRAPPER, + TOKEN_GENERATOR_GET_FLAGS_WRAPPER, + TOKEN_GENERATOR_POPULATE_LINE_WRAPPER, + /* + TOKEN_GENERATOR_COMPUTE_CURSOR_WRAPPER, + TOKEN_GENERATOR_CONTAIN_CURSOR_WRAPPER, + */ + TOKEN_GENERATOR_GET_FLAGS_METHOD, + TOKEN_GENERATOR_POPULATE_LINE_METHOD, + /* + TOKEN_GENERATOR_COMPUTE_CURSOR_METHOD, + TOKEN_GENERATOR_CONTAIN_CURSOR_METHOD, + */ { NULL } }; - static PyGetSetDef py_line_generator_getseters[] = { - LINE_GENERATOR_LINES_COUNT_ATTRIB, + static PyGetSetDef py_token_generator_getseters[] = { + TOKEN_GENERATOR_LINES_COUNT_ATTRIB, { NULL } }; - static PyTypeObject py_line_generator_type = { + static PyTypeObject py_token_generator_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.LineGenerator", + .tp_name = "pychrysalide.glibext.TokenGenerator", .tp_basicsize = sizeof(PyObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = LINE_GENERATOR_DOC, + .tp_doc = TOKEN_GENERATOR_DOC, - .tp_methods = py_line_generator_methods, - .tp_getset = py_line_generator_getseters, + .tp_methods = py_token_generator_methods, + .tp_getset = py_token_generator_getseters, }; - return &py_line_generator_type; + return &py_token_generator_type; } @@ -804,7 +832,7 @@ PyTypeObject *get_python_line_generator_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.glibext.LineGenerator'.* +* Description : Prend en charge l'objet 'pychrysalide.....TokenGenerator'. * * * * Retour : Bilan de l'opération. * * * @@ -812,21 +840,21 @@ PyTypeObject *get_python_line_generator_type(void) * * ******************************************************************************/ -bool ensure_python_line_generator_is_registered(void) +bool ensure_python_token_generator_is_registered(void) { - PyTypeObject *type; /* Type Python 'LineGenerator' */ + PyTypeObject *type; /* Type Python 'TokenGenerator'*/ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ static GInterfaceInfo info = { /* Paramètres d'inscription */ - .interface_init = (GInterfaceInitFunc)py_line_generator_interface_init, + .interface_init = (GInterfaceInitFunc)py_token_generator_interface_init, .interface_finalize = NULL, .interface_data = NULL, }; - type = get_python_line_generator_type(); + type = get_python_token_generator_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -834,7 +862,7 @@ bool ensure_python_line_generator_is_registered(void) dict = PyModule_GetDict(module); - if (!register_interface_for_pygobject(dict, G_TYPE_LINE_GENERATOR, type, &info)) + if (!register_interface_for_pygobject(dict, G_TYPE_TOKEN_GENERATOR, type, &info)) return false; } @@ -857,11 +885,11 @@ bool ensure_python_line_generator_is_registered(void) * * ******************************************************************************/ -int convert_to_line_generator(PyObject *arg, void *dst) +int convert_to_token_generator(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_line_generator_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_token_generator_type()); switch (result) { @@ -871,11 +899,11 @@ int convert_to_line_generator(PyObject *arg, void *dst) break; case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to line generator"); + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to token generator"); break; case 1: - *((GLineGenerator **)dst) = G_LINE_GENERATOR(pygobject_get(arg)); + *((GTokenGenerator **)dst) = G_TOKEN_GENERATOR(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/linegen.h b/plugins/pychrysalide/glibext/generator.h index bfad885..b2672a8 100644 --- a/plugins/pychrysalide/glibext/linegen.h +++ b/plugins/pychrysalide/glibext/generator.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * linegen.h - prototypes pour l'équivalent Python du fichier "glibext/linegen.h" + * generator.h - prototypes pour l'équivalent Python du fichier "glibext/generator.h" * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H #include <Python.h> @@ -32,14 +32,14 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_line_generator_type(void); +PyTypeObject *get_python_token_generator_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.LineGenerator'. */ -bool ensure_python_line_generator_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.glibext.TokenGenerator'. */ +bool ensure_python_token_generator_is_registered(void); /* Tente de convertir en générateur de lignes. */ -int convert_to_line_generator(PyObject *, void *); +int convert_to_token_generator(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H */ diff --git a/plugins/pychrysalide/glibext/hashable.c b/plugins/pychrysalide/glibext/hashable.c new file mode 100644 index 0000000..c870d55 --- /dev/null +++ b/plugins/pychrysalide/glibext/hashable.c @@ -0,0 +1,398 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.c - équivalent Python du fichier "glibext/hashable.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "hashable.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/hashable-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_hashable_object_interface_init(GHashableObjectInterface *, gpointer *); + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint py_hashable_object_hash_wrapper(const GHashableObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet l'empreinte d'un objet calculée par son parent. */ +static PyObject *py_hashable_object_parent_hash(PyObject *, PyObject *); + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static Py_hash_t py_hashable_object_hash(PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_hashable_object_interface_init(GHashableObjectInterface *iface, gpointer *unused) +{ +#define HASHABLE_OBJECT_DOC \ + "The HashableObject class defines a interface ensuring that a" \ + " customized hashing method is available for an object.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, HashableObject):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.HashableObject._hash().\n" + + iface->hash = py_hashable_object_hash_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : object = objet dont l'instance est à consulter. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static guint py_hashable_object_hash_wrapper(const GHashableObject *object) +{ + guint result; /* Valeur à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define HASHABLE_OBJECT_HASH_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _hash, "$self", \ + METH_NOARGS, \ + "Abstract method computing a hash from an object which is used" \ + " as the default implementation of the __hash__() method.\n" \ + "\n" \ + "The result has to be an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + pyret = run_python_method(pyobj, "_hash", NULL); + + if (pyret != NULL) + { + if (PyLong_Check(pyret)) + result = PyLong_AsUnsignedLong(pyret); + + else + PyErr_SetString(PyExc_TypeError, _("computed hash value has to be an unsigned integer")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet l'empreinte d'un objet calculée par son parent. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_hashable_object_parent_hash(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + GHashableObjectInterface *iface; /* Interface utilisée */ + GHashableObjectInterface *parent_iface; /* Interface parente */ + guint hash; /* Valeur d'empreitne */ + +#define HASHABLE_OBJECT_PARENT_HASH_METHOD PYTHON_METHOD_DEF \ +( \ + parent_hash, "$self", \ + METH_NOARGS, py_hashable_object, \ + "Provide the hash value defined by the interface implementation"\ + " from the object native parent.\n" \ + "\n" \ + "The result is an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.HashableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._hash() function has to be used" \ + " instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the HashableObject interface")); + + result = NULL; + + } + else + { + hash = parent_iface->hash(object); + + result = PyLong_FromUnsignedLong(hash); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static Py_hash_t py_hashable_object_hash(PyObject *self) +{ + Py_hash_t result; /* Empreinte à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + result = g_hashable_object_hash(object); + + UPDATE_RESULT_IF_RAISED_EXCEPTION(-1); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_hashable_object_type(void) +{ + static PyMethodDef py_hashable_object_methods[] = { + HASHABLE_OBJECT_HASH_WRAPPER, + HASHABLE_OBJECT_PARENT_HASH_METHOD, + { NULL } + }; + + static PyGetSetDef py_hashable_object_getseters[] = { + { NULL } + }; + + static PyTypeObject py_hashable_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.HashableObject", + .tp_basicsize = sizeof(PyObject), + + .tp_hash = py_hashable_object_hash, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = HASHABLE_OBJECT_DOC, + + .tp_methods = py_hashable_object_methods, + .tp_getset = py_hashable_object_getseters, + + }; + + return &py_hashable_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.....HashableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_hashable_object_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'HashableObject'*/ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_hashable_object_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_hashable_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_HASHABLE_OBJECT, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'objet réductible. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_hashable_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_hashable_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hashable object"); + break; + + case 1: + *((GHashableObject **)dst) = G_HASHABLE_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/comparison.h b/plugins/pychrysalide/glibext/hashable.h index 79f7092..8583118 100644 --- a/plugins/pychrysalide/glibext/comparison.h +++ b/plugins/pychrysalide/glibext/hashable.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour l'équivalent Python du fichier "glibext/comparison.h" + * hashable.h - prototypes pour l'équivalent Python du fichier "glibext/hashable.h" * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H #include <Python.h> @@ -32,14 +32,14 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_comparable_item_type(void); +PyTypeObject *get_python_hashable_object_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.ComparableItem'. */ -bool ensure_python_comparable_item_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.glibext.HashableObject'. */ +bool ensure_python_hashable_object_is_registered(void); -/* Tente de convertir en élément comparable. */ -int convert_to_comparable_item(PyObject *, void *); +/* Tente de convertir en interface d'objet réductible. */ +int convert_to_hashable_object(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H */ diff --git a/plugins/pychrysalide/glibext/module-ui.c b/plugins/pychrysalide/glibext/module-ui.c new file mode 100644 index 0000000..8fa6d0e --- /dev/null +++ b/plugins/pychrysalide/glibext/module-ui.c @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire glibext (forme graphique) en tant que module + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module-ui.h" + + +#include <assert.h> + + +#include "bufferline.h" +#include "generator.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'glibext' (mode UI). * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_glibext_module_ui(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_token_generator_is_registered(); + + if (result) result = ensure_python_buffer_line_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/pychrysalide/glibext/module-ui.h b/plugins/pychrysalide/glibext/module-ui.h new file mode 100644 index 0000000..d91a2fa --- /dev/null +++ b/plugins/pychrysalide/glibext/module-ui.h @@ -0,0 +1,38 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire glibext (forme graphique) en tant que module + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H + + +#include <Python.h> +#include <stdbool.h> + + +/* Intègre les objets du module 'glibext' (mode UI). */ +bool populate_glibext_module_ui(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H */ diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c index 3e4307a..bbe357d 100644 --- a/plugins/pychrysalide/glibext/module.c +++ b/plugins/pychrysalide/glibext/module.c @@ -28,18 +28,28 @@ #include <assert.h> +/* #include "binarycursor.h" -#include "binportion.h" #include "buffercache.h" #include "bufferline.h" #include "bufferview.h" -#include "comparison.h" #include "configuration.h" #include "linecursor.h" #include "linegen.h" #include "loadedpanel.h" #include "named.h" +*/ +#include "comparable.h" +#include "hashable.h" +#include "objhole.h" +#include "portion.h" +#include "secstorage.h" +#include "serialize.h" #include "singleton.h" +#include "storage.h" +#include "strbuilder.h" +#include "work.h" +#include "workqueue.h" #include "../helpers.h" @@ -106,16 +116,28 @@ bool populate_glibext_module(void) result = true; + if (result) result = ensure_python_comparable_object_is_registered(); + if (result) result = ensure_python_hashable_object_is_registered(); + if (result) result = ensure_python_serializable_object_is_registered(); if (result) result = ensure_python_singleton_candidate_is_registered(); + if (result) result = ensure_python_string_builder_is_registered(); - if (result) result = ensure_python_binary_cursor_is_registered(); + if (result) result = ensure_python_thick_object_is_registered(); if (result) result = ensure_python_binary_portion_is_registered(); + if (result) result = ensure_python_generic_work_is_registered(); + if (result) result = ensure_python_object_storage_is_registered(); + if (result) result = ensure_python_secret_storage_is_registered(); + if (result) result = ensure_python_singleton_factory_is_registered(); + if (result) result = ensure_python_work_queue_is_registered(); + + /* + + if (result) result = ensure_python_binary_cursor_is_registered(); if (result) result = ensure_python_buffer_cache_is_registered(); if (result) result = ensure_python_buffer_line_is_registered(); #ifdef INCLUDE_GTK_SUPPORT if (result) result = ensure_python_buffer_view_is_registered(); #endif - if (result) result = ensure_python_comparable_item_is_registered(); if (result) result = ensure_python_config_param_is_registered(); if (result) result = ensure_python_config_param_iterator_is_registered(); if (result) result = ensure_python_generic_config_is_registered(); @@ -125,7 +147,7 @@ bool populate_glibext_module(void) if (result) result = ensure_python_loaded_panel_is_registered(); if (result) result = ensure_python_named_widget_is_registered(); #endif - if (result) result = ensure_python_singleton_factory_is_registered(); + */ assert(result); diff --git a/plugins/pychrysalide/glibext/objhole.c b/plugins/pychrysalide/glibext/objhole.c new file mode 100644 index 0000000..6bea5d1 --- /dev/null +++ b/plugins/pychrysalide/glibext/objhole.c @@ -0,0 +1,429 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.c - équivalent Python du fichier "glibext/objhole.c" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "objhole.h" + + +#include <pygobject.h> + + +#include <i18n.h> +#include <glibext/objhole-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(thick_object, G_TYPE_THICK_OBJECT); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_thick_object_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ + + +/* Pose un verrou à l'aide du bit dédié de GObject. */ +static PyObject *py_thick_object_lock(PyObject *, PyObject *); + +/* Retire un verrou via le bit dédié de GObject. */ +static PyObject *py_thick_object_unlock(PyObject *, PyObject *); + +/* Indique le nombre de bits accaparés par la GLib. */ +static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *, void *); + +/* Fournit la valeur courante de la zone de stockage d'un objet. */ +static PyObject *py_thick_object_get_extra(PyObject *, void *); + +/* Définit la valeur courante de la zone de stockage d'un objet. */ +static int py_thick_object_set_extra(PyObject *, PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_thick_object_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define THICK_OBJECT_DOC \ + "The ThickObject class is an alternative version of the GObject class." \ + " It provides an access to a 32-bit wide memory hole inside the native" \ + " GObject structure in order to exploit the area as various values" \ + " storage.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " ThickObject()" + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* LIAISON DE FONCTIONNALITES AVEC L'API PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = instance représentant une extension d'objet. * +* args = arguments fournis à l'appel, non utilisé ici. * +* * +* Description : Pose un verrou à l'aide du bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_lock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + +#define THICK_OBJECT_LOCK_METHOD PYTHON_METHOD_DEF \ +( \ + lock, "$self", \ + METH_NOARGS, py_thick_object, \ + "Lock the object using the internal GLib bit.\n" \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_lock(obj); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance représentant une extension d'objet. * +* args = arguments fournis à l'appel, non utilisé ici. * +* * +* Description : Retire un verrou via le bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_unlock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + +#define THICK_OBJECT_UNLOCK_METHOD PYTHON_METHOD_DEF \ +( \ + unlock, "$self", \ + METH_NOARGS, py_thick_object, \ + "Unlock the object using the internal GLib bit.\n" \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_unlock(obj); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le nombre de bits accaparés par la GLib. * +* * +* Retour : Nombre de bits, à priori inférieur à 32. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *self, void *closure) +{ + PyObject *result; /* Résultat à retourner */ + +#define THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + _GOBJECT_RESERVED_EXTRA_BITS, py_thick_object, \ + "Quantity of lower bits used by the GLib inside the memory hole.\n" \ + "\n" \ + "The returned value should be less then 32 and is provided for" \ + " pychrysalide.glibext.ThickObject subclass implementation." \ +) + + result = PyLong_FromUnsignedLong(GOBJECT_RESERVED_EXTRA_BITS); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : Valeur de 32 lues et expurgées des bits GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_get_extra(PyObject *self, void *closure) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + guint extra; /* Valeur à transmettre */ + +#define THICK_OBJECT_EXTRA_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + extra, py_thick_object, \ + "Data stored inside the structure memory hole of all GObject" \ + " native instances.\n" \ + "\n" \ + "Less than 32 bits are available for storing arbitrary values." \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + extra = g_thick_object_get_extra(obj); + + result = PyLong_FromUnsignedLong(extra); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* value = valeur fournie à intégrer ou prendre en compte. * +* closure = non utilisé ici. * +* * +* Description : Définit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_thick_object_set_extra(PyObject *self, PyObject *value, void *closure) +{ + unsigned long extra; /* Valeur à inscrire */ + GThickObject *obj; /* Version GLib de l'instance */ + + if (!PyLong_Check(value)) + { + PyErr_SetString(PyExc_TypeError, _("Invalid integer value")); + return -1; + } + + extra = PyLong_AsUnsignedLong(value); + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_set_extra(obj, extra); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_thick_object_type(void) +{ + static PyMethodDef py_thick_object_methods[] = { + THICK_OBJECT_LOCK_METHOD, + THICK_OBJECT_UNLOCK_METHOD, + { NULL } + }; + + static PyGetSetDef py_thick_object_getseters[] = { + THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB, + THICK_OBJECT_EXTRA_ATTRIB, + { NULL } + }; + + static PyTypeObject py_thick_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ThickObject", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = THICK_OBJECT_DOC, + + .tp_methods = py_thick_object_methods, + .tp_getset = py_thick_object_getseters, + + .tp_init = py_thick_object_init, + .tp_new = py_thick_object_new, + + }; + + return &py_thick_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_thick_object_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'ThickObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_thick_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_THICK_OBJECT, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en objet dense. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_thick_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_thick_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to thick object"); + break; + + case 1: + *((GThickObject **)dst) = G_THICK_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/objhole.h b/plugins/pychrysalide/glibext/objhole.h new file mode 100644 index 0000000..b541b70 --- /dev/null +++ b/plugins/pychrysalide/glibext/objhole.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.h - prototypes pour l'équivalent Python du fichier "glibext/objhole.h" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_thick_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. */ +bool ensure_python_thick_object_is_registered(void); + +/* Tente de convertir en objet dense. */ +int convert_to_thick_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H */ diff --git a/plugins/pychrysalide/glibext/binportion.c b/plugins/pychrysalide/glibext/portion.c index 70eb314..d95308c 100644 --- a/plugins/pychrysalide/glibext/binportion.c +++ b/plugins/pychrysalide/glibext/portion.c @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * binportion.c - équivalent Python du fichier "glibext/gbinportion.c" + * portion.c - équivalent Python du fichier "glibext/portion.c" * * Copyright (C) 2019-2020 Cyrille Bagard * @@ -22,15 +22,14 @@ */ -#include "binportion.h" +#include "portion.h" #include <pygobject.h> #include <i18n.h> -#include <glibext/gbinportion-int.h> -#include <plugins/dt.h> +#include <glibext/portion-int.h> #include "constants.h" @@ -40,13 +39,13 @@ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_bin_portion_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(binary_portion, G_TYPE_BINARY_PORTION); + /* Initialise une instance sur la base du dérivé de GObject. */ -static int py_bin_portion_init(PyObject *, PyObject *, PyObject *); +static int py_binary_portion_init(PyObject *, PyObject *, PyObject *); -/* Effectue une comparaison avec un objet Python 'BinPortion'. */ +/* Effectue une comparaison avec un objet Python BinaryPortion. */ static PyObject *py_binary_portion_richcompare(PyObject *, PyObject *, int); /* Assure qu'une portion ne dépasse pas une position donnée. */ @@ -77,66 +76,6 @@ static int py_binary_portion_set_rights(PyObject *, PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_bin_portion_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_binary_portion_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_BIN_PORTION, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -149,33 +88,30 @@ static PyObject *py_bin_portion_new(PyTypeObject *type, PyObject *args, PyObject * * ******************************************************************************/ -static int py_bin_portion_init(PyObject *self, PyObject *args, PyObject *kwds) +static int py_binary_portion_init(PyObject *self, PyObject *args, PyObject *kwds) { - const char *code; /* Identifiant de couleur */ vmpa2t *addr; /* Emplacement de portion */ unsigned long long size; /* Taille de la portion */ int ret; /* Bilan de lecture des args. */ - GBinPortion *portion; /* Portion à manipuler */ + GBinaryPortion *portion; /* Portion à manipuler */ - static char *kwlist[] = { "code", "addr", "size", NULL }; + static char *kwlist[] = { "addr", "size", NULL }; #define BINARY_PORTION_DOC \ - "The BinPortion object handles parts of binaries usually formally" \ + "The BinaryPortion object handles parts of binaries usually formally" \ " identified in binary formats, like program segments or sections for ELF" \ " files for example.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " BinPortion(code, addr, size)" \ + " BinaryPortion(addr, size)" \ "\n" \ - "Where code is the CSS class style for the rendering color to use, addr is" \ - " the starting point of the portion in memory, as a pychrysalide.arch.vmpa" \ - " value, and size is the size of the portion." \ + "Where *addr* is the starting point of the portion in memory, as a" \ + " pychrysalide.arch.vmpa value, and *size* is the size of the portion." /* Récupération des paramètres */ - ret = PyArg_ParseTupleAndKeywords(args, kwds, "sO&K", kwlist, - &code, convert_any_to_vmpa, &addr, &size); + ret = PyArg_ParseTupleAndKeywords(args, kwds, "O&K", kwlist, convert_any_to_vmpa, &addr, &size); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -189,11 +125,13 @@ static int py_bin_portion_init(PyObject *self, PyObject *args, PyObject *kwds) /* Eléments de base */ - portion = G_BIN_PORTION(pygobject_get(self)); - - portion->code = strdup(code); + portion = G_BINARY_PORTION(pygobject_get(self)); - init_mrange(&portion->range, addr, size); + if (!g_binary_portion_create(portion, addr, size)) + { + clean_vmpa_arg(addr); + return -1; + } clean_vmpa_arg(addr); @@ -208,7 +146,7 @@ static int py_bin_portion_init(PyObject *self, PyObject *args, PyObject *kwds) * b = second object Python à consulter. * * op = type de comparaison menée. * * * -* Description : Effectue une comparaison avec un objet Python 'BinPortion'. * +* Description : Effectue une comparaison avec un objet Python BinaryPortion. * * * * Retour : Bilan de l'opération. * * * @@ -220,8 +158,8 @@ static PyObject *py_binary_portion_richcompare(PyObject *a, PyObject *b, int op) { PyObject *result; /* Bilan à retourner */ int ret; /* Bilan de lecture des args. */ - const GBinPortion *portion_a; /* Premier élément à traiter */ - const GBinPortion *portion_b; /* Second élément à traiter */ + const GBinaryPortion *portion_a; /* Premier élément à traiter */ + const GBinaryPortion *portion_b; /* Second élément à traiter */ int status; /* Résultat d'une comparaison */ ret = PyObject_IsInstance(b, (PyObject *)get_python_binary_portion_type()); @@ -231,8 +169,8 @@ static PyObject *py_binary_portion_richcompare(PyObject *a, PyObject *b, int op) goto cmp_done; } - portion_a = G_BIN_PORTION(pygobject_get(a)); - portion_b = G_BIN_PORTION(pygobject_get(b)); + portion_a = G_BINARY_PORTION(pygobject_get(a)); + portion_b = G_BINARY_PORTION(pygobject_get(b)); status = g_binary_portion_compare(&portion_a, &portion_b); @@ -265,16 +203,16 @@ static PyObject *py_binary_portion_limit_range(PyObject *self, PyObject *args) PyObject *result; /* Trouvailles à retourner */ unsigned long long max; /* Taille maximale à accorder */ int ret; /* Bilan de lecture des args. */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ bool status; /* Bilan de la modification */ -#define BINARY_SYMBOL_LIMIT_RANGE_METHOD PYTHON_METHOD_DEF \ +#define BINARY_PORTION_LIMIT_RANGE_METHOD PYTHON_METHOD_DEF \ ( \ limit_range, "$self, max, /", \ METH_VARARGS, py_binary_portion, \ "Ensure the portion range does not cross a boundary size.\n" \ "\n" \ - "An integer value is expected as the maximum size of the" \ + "An *max* integer value is expected as the maximum size of the" \ " portion.\n" \ "\n" \ "A boolean value indicating the success of the operation is" \ @@ -284,7 +222,7 @@ static PyObject *py_binary_portion_limit_range(PyObject *self, PyObject *args) ret = PyArg_ParseTuple(args, "K", &max); if (!ret) return NULL; - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); status = g_binary_portion_limit_range(portion, max); @@ -311,24 +249,27 @@ static PyObject *py_binary_portion_limit_range(PyObject *self, PyObject *args) static PyObject *py_binary_portion_include(PyObject *self, PyObject *args) { - GBinPortion *sub; /* Sous-portion à inclure */ + GBinaryPortion *sub; /* Sous-portion à inclure */ int ret; /* Bilan de lecture des args. */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ -#define BINARY_SYMBOL_INCLUDE_METHOD PYTHON_METHOD_DEF \ -( \ - include, "$self, sub, /", \ - METH_VARARGS, py_binary_portion, \ - "Include another binary portion as a child item.\n" \ - "\n" \ - "The sub portion has to be a pychrysalide.glibext.BinPortion" \ - " instance." \ +#define BINARY_PORTION_INCLUDE_METHOD PYTHON_METHOD_DEF \ +( \ + include, "$self, sub, /", \ + METH_VARARGS, py_binary_portion, \ + "Include another binary portion as a child item.\n" \ + "\n" \ + "The provided *sub* portion has to be a" \ + " pychrysalide.glibext.BinaryPortion instance." \ + "\n" \ + "The returned value is a boolean value providing the" \ + " status of the operation." \ ) ret = PyArg_ParseTuple(args, "O&", convert_to_binary_portion, &sub); if (!ret) return NULL; - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); g_object_ref(G_OBJECT(sub)); g_binary_portion_include(portion, sub); @@ -354,7 +295,7 @@ static PyObject *py_binary_portion_include(PyObject *self, PyObject *args) static PyObject *py_binary_portion_get_desc(PyObject *self, void *closure) { PyObject *result; /* Résultat à retourner */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ const char *desc; /* Description récupérée */ #define BINARY_PORTION_DESC_ATTRIB PYTHON_GETSET_DEF_FULL \ @@ -364,7 +305,7 @@ static PyObject *py_binary_portion_get_desc(PyObject *self, void *closure) " simple string." \ ) - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); desc = g_binary_portion_get_desc(portion); @@ -391,7 +332,7 @@ static PyObject *py_binary_portion_get_desc(PyObject *self, void *closure) static int py_binary_portion_set_desc(PyObject *self, PyObject *value, void *closure) { - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ const char *desc; /* Description à définir */ if (!PyUnicode_Check(value)) @@ -400,7 +341,7 @@ static int py_binary_portion_set_desc(PyObject *self, PyObject *value, void *clo return -1; } - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); desc = PyUnicode_DATA(value); @@ -427,7 +368,7 @@ static int py_binary_portion_set_desc(PyObject *self, PyObject *value, void *clo static PyObject *py_binary_portion_get_range(PyObject *self, void *closure) { PyObject *result; /* Résultat à retourner */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ const mrange_t *range; /* Espace de couverture */ #define BINARY_PORTION_RANGE_ATTRIB PYTHON_GET_DEF_FULL \ @@ -438,7 +379,7 @@ static PyObject *py_binary_portion_get_range(PyObject *self, void *closure) "This property is a pychrysalide.arch.mrange instance." \ ) - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); range = g_binary_portion_get_range(portion); @@ -465,7 +406,7 @@ static PyObject *py_binary_portion_get_range(PyObject *self, void *closure) static PyObject *py_binary_portion_get_continuation(PyObject *self, void *closure) { PyObject *result; /* Résultat à retourner */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ bool status; /* Bilan d'une consultation */ #define BINARY_PORTION_CONTINUATION_ATTRIB PYTHON_GETSET_DEF_FULL \ @@ -478,7 +419,7 @@ static PyObject *py_binary_portion_get_continuation(PyObject *self, void *closur " several parts when included in the portion tree." \ ) - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); status = g_binary_portion_is_continuation(portion); @@ -506,7 +447,7 @@ static PyObject *py_binary_portion_get_continuation(PyObject *self, void *closur static int py_binary_portion_set_continuation(PyObject *self, PyObject *value, void *closure) { - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ bool status; /* Valeur à manipuler */ if (!PyBool_Check(value)) @@ -515,7 +456,7 @@ static int py_binary_portion_set_continuation(PyObject *self, PyObject *value, v return -1; } - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); status = (value == Py_True); @@ -542,17 +483,17 @@ static int py_binary_portion_set_continuation(PyObject *self, PyObject *value, v static PyObject *py_binary_portion_get_rights(PyObject *self, void *closure) { PyObject *result; /* Résultat à retourner */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ PortionAccessRights rights; /* Bilan d'une consultation */ #define BINARY_PORTION_RIGHTS_ATTRIB PYTHON_GETSET_DEF_FULL \ ( \ rights, py_binary_portion, \ "Access rights declared for the binary portion, as a" \ - " pychrysalide.glibext.BinPortion.PortionAccessRights value." \ + " pychrysalide.glibext.BinaryPortion.PortionAccessRights value."\ ) - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); rights = g_binary_portion_get_rights(portion); @@ -579,13 +520,13 @@ static PyObject *py_binary_portion_get_rights(PyObject *self, void *closure) static int py_binary_portion_set_rights(PyObject *self, PyObject *value, void *closure) { - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ PortionAccessRights rights; /* Valeur à manipuler */ if (convert_to_portion_access_rights(value, &rights) != 1) return -1; - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); g_binary_portion_set_rights(portion, rights); @@ -609,8 +550,8 @@ static int py_binary_portion_set_rights(PyObject *self, PyObject *value, void *c PyTypeObject *get_python_binary_portion_type(void) { static PyMethodDef py_binary_portion_methods[] = { - BINARY_SYMBOL_LIMIT_RANGE_METHOD, - BINARY_SYMBOL_INCLUDE_METHOD, + BINARY_PORTION_LIMIT_RANGE_METHOD, + BINARY_PORTION_INCLUDE_METHOD, { NULL } }; @@ -626,7 +567,7 @@ PyTypeObject *get_python_binary_portion_type(void) PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.BinPortion", + .tp_name = "pychrysalide.glibext.BinaryPortion", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, @@ -638,8 +579,8 @@ PyTypeObject *get_python_binary_portion_type(void) .tp_methods = py_binary_portion_methods, .tp_getset = py_binary_portion_getseters, - .tp_init = py_bin_portion_init, - .tp_new = py_bin_portion_new, + .tp_init = py_binary_portion_init, + .tp_new = py_binary_portion_new, }; @@ -652,7 +593,7 @@ PyTypeObject *get_python_binary_portion_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.glibext.BinPortion'. * +* Description : Prend en charge l'objet 'pychrysalide.glibext.BinaryPortion'.* * * * Retour : Bilan de l'opération. * * * @@ -662,7 +603,7 @@ PyTypeObject *get_python_binary_portion_type(void) bool ensure_python_binary_portion_is_registered(void) { - PyTypeObject *type; /* Type Python 'BinPortion' */ + PyTypeObject *type; /* Type Python 'BinaryPortion' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ @@ -674,7 +615,7 @@ bool ensure_python_binary_portion_is_registered(void) dict = PyModule_GetDict(module); - if (!register_class_for_pygobject(dict, G_TYPE_BIN_PORTION, type)) + if (!register_class_for_pygobject(dict, G_TYPE_BINARY_PORTION, type)) return false; if (!define_binary_portion_constants(type)) @@ -718,7 +659,7 @@ int convert_to_binary_portion(PyObject *arg, void *dst) break; case 1: - *((GBinPortion **)dst) = G_BIN_PORTION(pygobject_get(arg)); + *((GBinaryPortion **)dst) = G_BINARY_PORTION(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/binportion.h b/plugins/pychrysalide/glibext/portion.h index b27c8ea..a417c01 100644 --- a/plugins/pychrysalide/glibext/binportion.h +++ b/plugins/pychrysalide/glibext/portion.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * binportion.h - prototypes pour l'équivalent Python du fichier "glibext/gbinportion.h" + * portion.h - prototypes pour l'équivalent Python du fichier "glibext/portion.h" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_BINPORTION_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_BINPORTION_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_PORTION_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_PORTION_H #include <Python.h> @@ -34,7 +34,7 @@ /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_binary_portion_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.BinPortion'. */ +/* Prend en charge l'objet 'pychrysalide.glibext.BinaryPortion'. */ bool ensure_python_binary_portion_is_registered(void); /* Tente de convertir en portion de binaire. */ @@ -42,4 +42,4 @@ int convert_to_binary_portion(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_BINPORTION_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_PORTION_H */ diff --git a/plugins/pychrysalide/glibext/secstorage.c b/plugins/pychrysalide/glibext/secstorage.c new file mode 100644 index 0000000..5935d29 --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.c @@ -0,0 +1,625 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - équivalent Python du fichier "glibext/secstorage.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "secstorage.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/secstorage-int.h> + + +#include "../access.h" +#include "../convert.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(secret_storage, G_TYPE_SECRET_STORAGE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_secret_storage_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Définit un mot de passe pour protéger une clef maître. */ +static PyObject *py_secret_storage_set_password(PyObject *, PyObject *); + +/* Déverrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_unlock(PyObject *, PyObject *); + +/* Verrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_lock(PyObject *, PyObject *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_encrypt_data(PyObject *, PyObject *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_decrypt_data(PyObject *, PyObject *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +static PyObject *py_secret_storage_has_key(PyObject *, void *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +static PyObject *py_secret_storage_is_locked(PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_secret_storage_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GSettings *settings; /* Configuration à considérer */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage natif à manipuler */ + +#define SECRET_STORAGE_DOC \ + "SecretStorage acts as guardian for secrets using ecryption," \ + " mainly for sensitive information stored as configuration" \ + " parameters.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " SecretStorage(settings=None)" \ + "\n" \ + "The *settings* arguement may point to a GSettings instance." \ + " This optional argument is mainly used for testing purpose;" \ + " the main configuration settings are used by default." + + settings = NULL; + + ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings_or_none, &settings); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + if (!g_secret_storage_create(storage, settings)) + return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_set_password(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_SET_PASSWORD_METHOD PYTHON_METHOD_DEF \ +( \ + set_password, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Create a master key used for protecting secrets. This key is" \ + " itself protected by the provided password.\n" \ + "\n" \ + "The supplied *password* has to be a string.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed,"\ + " *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_set_password(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_unlock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_UNLOCK_METHOD PYTHON_METHOD_DEF \ +( \ + unlock, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt in memory the master key used for protecting secrets.\n" \ + "\n" \ + "The supplied *password* is the primary password used to protect" \ + " this key.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed" \ + " or if the master key is already unlocked, *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_unlock(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_lock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + +#define SECRET_STORAGE_LOCK_METHOD PYTHON_METHOD_DEF \ +( \ + lock, "", \ + METH_NOARGS, py_secret_storage, \ + "Clear from memory the master key used for protecting secrets." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + g_secret_storage_lock(storage); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_encrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_ENCRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + encrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Encrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either encrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_encrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_decrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_DECRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + decrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either decrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_decrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_has_key(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define PY_SECRET_STORAGE_HAS_KEY_ATTRIB PYTHON_HAS_DEF_FULL \ +( \ + key, py_secret_storage, \ + "Indicate if a master key used for protecting secrets seems to have"\ + " been defined. Without any unlocking attempt, the test only relies"\ + " on the length of saved master data.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " seems to exist, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_has_key(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_is_locked(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define SECRET_STORAGE_IS_LOCKED_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + locked, py_secret_storage, \ + "Indicate if the master key used for protecting secrets is" \ + " currently decrypted in memory.\n" \ + "\n" \ + "The *settings* arguement must point to a GSettings intance; the" \ + " main configuration settings are used by default.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " is unlocked and ready for use, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_is_locked(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_secret_storage_type(void) +{ + static PyMethodDef py_secret_storage_methods[] = { + SECRET_STORAGE_SET_PASSWORD_METHOD, + SECRET_STORAGE_UNLOCK_METHOD, + SECRET_STORAGE_LOCK_METHOD, + SECRET_STORAGE_ENCRYPT_DATA_METHOD, + SECRET_STORAGE_DECRYPT_DATA_METHOD, + { NULL } + }; + + static PyGetSetDef py_secret_storage_getseters[] = { + PY_SECRET_STORAGE_HAS_KEY_ATTRIB, + SECRET_STORAGE_IS_LOCKED_ATTRIB, + { NULL } + }; + + static PyTypeObject py_secret_storage_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.SecretStorage", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = SECRET_STORAGE_DOC, + + .tp_methods = py_secret_storage_methods, + .tp_getset = py_secret_storage_getseters, + + .tp_init = py_secret_storage_init, + .tp_new = py_secret_storage_new, + + }; + + return &py_secret_storage_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_secret_storage_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'SecretStorage' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_secret_storage_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_SECRET_STORAGE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en gardien des secrets avec stockage. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_secret_storage(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_secret_storage_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to secret storage"); + break; + + case 1: + *((GSecretStorage **)dst) = G_SECRET_STORAGE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/secstorage.h b/plugins/pychrysalide/glibext/secstorage.h new file mode 100644 index 0000000..68726c3 --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour l'équivalent Python du fichier "glibext/secstorage.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_secret_storage_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'. */ +bool ensure_python_secret_storage_is_registered(void); + +/* Tente de convertir en gardien des secrets avec stockage. */ +int convert_to_secret_storage(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H */ diff --git a/plugins/pychrysalide/analysis/storage/serialize.c b/plugins/pychrysalide/glibext/serialize.c index 40fcef7..61f359f 100644 --- a/plugins/pychrysalide/analysis/storage/serialize.c +++ b/plugins/pychrysalide/glibext/serialize.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * serialize.c - équivalent Python du fichier "analysis/storage/serialize.h" + * serialize.c - équivalent Python du fichier "glibext/serialize.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,13 +28,12 @@ #include <pygobject.h> -#include <analysis/storage/serialize-int.h> +#include <glibext/serialize-int.h> #include "storage.h" -#include "../../access.h" -#include "../../helpers.h" -#include "../../common/packed.h" +#include "../access.h" +#include "../helpers.h" @@ -42,23 +41,23 @@ /* Procède à l'initialisation de l'interface de génération. */ -static void py_serializable_object_interface_init(GSerializableObjectIface *, gpointer *); +static void py_serializable_object_interface_init(GSerializableObjectInterface *, gpointer *); -/* Charge un objet depuis une mémoire tampon. */ -static bool py_serializable_object_load_wrapper(GSerializableObject *, GObjectStorage *, packed_buffer_t *); +/* Charge un objet depuis un flux de données. */ +static bool py_serializable_object_load_wrapper(GSerializableObject *, GObjectStorage *, int); -/* Sauvegarde un objet dans une mémoire tampon. */ -static bool py_serializable_object_store_wrapper(const GSerializableObject *, GObjectStorage *, packed_buffer_t *); +/* Sauvegarde un objet dans un flux de données. */ +static bool py_serializable_object_store_wrapper(const GSerializableObject *, GObjectStorage *, int); /* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ -/* Charge un objet depuis une mémoire tampon. */ +/* Charge un objet depuis un flux de données. */ static bool py_serializable_object_load(PyObject *, PyObject *); -/* Sauvegarde un objet dans une mémoire tampon. */ +/* Sauvegarde un objet dans un flux de données. */ static bool py_serializable_object_store(PyObject *, PyObject *); @@ -81,7 +80,7 @@ static bool py_serializable_object_store(PyObject *, PyObject *); * * ******************************************************************************/ -static void py_serializable_object_interface_init(GSerializableObjectIface *iface, gpointer *unused) +static void py_serializable_object_interface_init(GSerializableObjectInterface *iface, gpointer *unused) { #define SERIALIZABLE_OBJECT_DOC \ @@ -94,8 +93,8 @@ static void py_serializable_object_interface_init(GSerializableObjectIface *ifac " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ - "* pychrysalide.analysis.storage.SerializableObject._load();\n" \ - "* pychrysalide.analysis.storage.SerializableObject._store();\n" + "* pychrysalide.glibext.SerializableObject._load();\n" \ + "* pychrysalide.glibext.SerializableObject._store();\n" iface->load = py_serializable_object_load_wrapper; iface->store = py_serializable_object_store_wrapper; @@ -106,10 +105,10 @@ static void py_serializable_object_interface_init(GSerializableObjectIface *ifac /****************************************************************************** * * * Paramètres : object = instruction d'assemblage à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * * * -* Description : Charge un objet depuis une mémoire tampon. * +* Description : Charge un objet depuis un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -117,25 +116,24 @@ static void py_serializable_object_interface_init(GSerializableObjectIface *ifac * * ******************************************************************************/ -static bool py_serializable_object_load_wrapper(GSerializableObject *object, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool py_serializable_object_load_wrapper(GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *storage_obj; /* Objet Python à emmployer */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ #define SERIALIZABLE_OBJECT_LOAD_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _load, "$self, storage, pbuf, /", \ + _load, "$self, storage, fd, /", \ METH_VARARGS, \ - "Abstract method used to load an object definition from buffered data.\n" \ + "Abstract method used to load an object definition from a data stream.\n" \ "\n" \ - "The *storage* is a pychrysalide.analysis.storage.ObjectStorage instance" \ - " provided to store inner objects, if relevant, or None. The *pbuf*" \ - " argument points to a pychrysalide.common.PackedBuffer object containing" \ - " the data to process.\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " provided as a file descriptor which as to be kept open after" \ + " processing.\n" \ "\n" \ "The result is a boolean indicating the status of the operation." \ ) @@ -148,17 +146,9 @@ static bool py_serializable_object_load_wrapper(GSerializableObject *object, GOb if (has_python_method(pyobj, "_load")) { - if (storage == NULL) - { - storage_obj = Py_None; - Py_INCREF(storage_obj); - } - else - storage_obj = pygobject_new(G_OBJECT(storage)); - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, storage_obj); - PyTuple_SetItem(args, 1, build_from_internal_packed_buffer(pbuf)); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(storage))); + PyTuple_SetItem(args, 1, PyLong_FromLong(fd)); pyret = run_python_method(pyobj, "_load", args); @@ -182,10 +172,10 @@ static bool py_serializable_object_load_wrapper(GSerializableObject *object, GOb /****************************************************************************** * * * Paramètres : object = instruction d'assemblage à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * * * -* Description : Sauvegarde un objet dans une mémoire tampon. * +* Description : Sauvegarde un objet dans un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -193,25 +183,24 @@ static bool py_serializable_object_load_wrapper(GSerializableObject *object, GOb * * ******************************************************************************/ -static bool py_serializable_object_store_wrapper(const GSerializableObject *object, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool py_serializable_object_store_wrapper(const GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *storage_obj; /* Objet Python à emmployer */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ #define SERIALIZABLE_OBJECT_STORE_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _store, "$self, storage, pbuf, /", \ + _store, "$self, storage, fd, /", \ METH_VARARGS, \ - "Abstract method used to store an object definition into buffered data.\n" \ + "Abstract method used to store an object definition into a data stream.\n" \ "\n" \ - "The *storage* is a pychrysalide.analysis.storage.ObjectStorage instance" \ - " provided to store inner objects, if relevant, or None. The *pbuf*" \ - " argument points to a pychrysalide.common.PackedBuffer object containing" \ - " the data to process.\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " provided as a file descriptor which as to be kept open after" \ + " processing.\n" \ "\n" \ "The result is a boolean indicating the status of the operation." \ ) @@ -224,17 +213,9 @@ static bool py_serializable_object_store_wrapper(const GSerializableObject *obje if (has_python_method(pyobj, "_store")) { - if (storage == NULL) - { - storage_obj = Py_None; - Py_INCREF(storage_obj); - } - else - storage_obj = pygobject_new(G_OBJECT(storage)); - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, storage_obj); - PyTuple_SetItem(args, 1, build_from_internal_packed_buffer(pbuf)); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(storage))); + PyTuple_SetItem(args, 1, PyLong_FromLong(fd)); pyret = run_python_method(pyobj, "_store", args); @@ -266,7 +247,7 @@ static bool py_serializable_object_store_wrapper(const GSerializableObject *obje * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Charge un objet depuis une mémoire tampon. * +* Description : Charge un objet depuis un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -278,32 +259,30 @@ static bool py_serializable_object_load(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ GObjectStorage *storage; /* Conservateur à manipuler */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + int fd; /* Flux ouvert (en lecture) */ int ret; /* Bilan de lecture des args. */ GSerializableObject *object; /* Version native */ bool status; /* Bilan de l'opération */ #define SERIALIZABLE_OBJECT_LOAD_METHOD PYTHON_METHOD_DEF \ ( \ - load, "$self, storage, pbuf, /", \ + load, "$self, storage, fd, /", \ METH_VARARGS, py_serializable_object, \ - "Load an object definition from buffered data.\n" \ + "Load an object definition from a data stream.\n" \ "\n" \ - "The *storage* is a pychrysalide.analysis.storage.ObjectStorage instance" \ - " provided to store inner objects, if relevant, or None. The *pbuf*" \ - " argument points to a pychrysalide.common.PackedBuffer object containing" \ - " the data to process.\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " used as a file descriptor for writing data\n" \ "\n" \ "The result is a boolean indicating the status of the operation." \ ) - ret = PyArg_ParseTuple(args, "O&O&", convert_to_object_storage_or_none, &storage, - convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "O&i", convert_to_object_storage, &storage, &fd); if (!ret) return NULL; object = G_SERIALIZABLE_OBJECT(pygobject_get(self)); - status = g_serializable_object_load(object, storage, pbuf); + status = g_serializable_object_load(object, storage, fd); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -318,7 +297,7 @@ static bool py_serializable_object_load(PyObject *self, PyObject *args) * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Sauvegarde un objet dans une mémoire tampon. * +* Description : Sauvegarde un objet dans un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -330,32 +309,30 @@ static bool py_serializable_object_store(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ GObjectStorage *storage; /* Conservateur à manipuler */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + int fd; /* Flux ouvert (en lecture) */ int ret; /* Bilan de lecture des args. */ GSerializableObject *object; /* Version native */ bool status; /* Bilan de l'opération */ #define SERIALIZABLE_OBJECT_STORE_METHOD PYTHON_METHOD_DEF \ ( \ - store, "$self, storage, pbuf, /", \ + store, "$self, storage, fd, /", \ METH_VARARGS, py_serializable_object, \ - "Store an object definition into buffered data.\n" \ + "Store an object definition into a data stream.\n" \ "\n" \ - "The *storage* is a pychrysalide.analysis.storage.ObjectStorage instance" \ - " provided to store inner objects, if relevant, or None. The *pbuf*" \ - " argument points to a pychrysalide.common.PackedBuffer object containing" \ - " the data to process.\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " used as a file descriptor for writing data\n" \ "\n" \ "The result is a boolean indicating the status of the operation." \ ) - ret = PyArg_ParseTuple(args, "O&O&", convert_to_object_storage_or_none, &storage, - convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "O&i", convert_to_object_storage, &storage, &fd); if (!ret) return NULL; object = G_SERIALIZABLE_OBJECT(pygobject_get(self)); - status = g_serializable_object_store(object, storage, pbuf); + status = g_serializable_object_store(object, storage, fd); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -395,7 +372,7 @@ PyTypeObject *get_python_serializable_object_type(void) PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.analysis.storage.SerializableObject", + .tp_name = "pychrysalide.glibext.SerializableObject", .tp_basicsize = sizeof(PyObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, @@ -442,7 +419,7 @@ bool ensure_python_serializable_object_is_registered(void) if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { - module = get_access_to_python_module("pychrysalide.analysis.storage"); + module = get_access_to_python_module("pychrysalide.glibext"); dict = PyModule_GetDict(module); diff --git a/plugins/pychrysalide/analysis/storage/serialize.h b/plugins/pychrysalide/glibext/serialize.h index 7e831e5..90688ba 100644 --- a/plugins/pychrysalide/analysis/storage/serialize.h +++ b/plugins/pychrysalide/glibext/serialize.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * serialize.h - prototypes pour l'équivalent Python du fichier "analysis/storage/serialize.h" + * serialize.h - prototypes pour l'équivalent Python du fichier "glibext/serialize.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_SERIALIZE_H -#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_SERIALIZE_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H #include <Python.h> @@ -34,7 +34,7 @@ /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_serializable_object_type(void); -/* Prend en charge l'objet 'pychrysalide.analysis.storage.SerializableObject'. */ +/* Prend en charge l'objet 'pychrysalide.glibext.SerializableObject'. */ bool ensure_python_serializable_object_is_registered(void); /* Tente de convertir en objet adapté à une mise en cache. */ @@ -42,4 +42,4 @@ int convert_to_serializable_object(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_SERIALIZE_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H */ diff --git a/plugins/pychrysalide/glibext/singleton.c b/plugins/pychrysalide/glibext/singleton.c index 8491473..8712506 100644 --- a/plugins/pychrysalide/glibext/singleton.c +++ b/plugins/pychrysalide/glibext/singleton.c @@ -30,7 +30,6 @@ #include <glibext/singleton-int.h> -#include <plugins/dt.h> #include "../access.h" @@ -42,7 +41,7 @@ /* Procède à l'initialisation de l'interface de candidature. */ -static void py_singleton_candidate_interface_init(GSingletonCandidateIface *, gpointer *); +static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *, gpointer *); /* Fournit une liste de candidats embarqués par un candidat. */ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper(const GSingletonCandidate *, size_t *); @@ -50,37 +49,27 @@ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper /* Met à jour une liste de candidats embarqués par un candidat. */ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *, const GSingletonCandidate *); - /* Marque un candidat comme figé. */ -static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *); +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *); /* Indique si le candidat est figé. */ -static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *); - -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static PyObject *py_singleton_candidate_hash(PyObject *, PyObject *); - -/* Fournit une liste de candidats embarqués par un candidat. */ -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *, void *); +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *); /* Indique si le candidat est figé. */ -static PyObject *py_singleton_candidate_get_read_only(PyObject *, void *); +static PyObject *py_singleton_candidate_is_read_only(PyObject *, void *); + +/* Crée une copie modifiable d'un object unique. */ +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *); -/* Effectue une comparaison avec un objet 'SingletonCandidate'. */ -static PyObject *py_singleton_candidate_richcompare(PyObject *, PyObject *, int); +/* Crée une copie modifiable d'un object unique. */ +static PyObject *py_singleton_candidate_dup(PyObject *, PyObject *); /* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_singleton_factory_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(singleton_factory, G_TYPE_SINGLETON_FACTORY); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_singleton_factory_init(PyObject *, PyObject *, PyObject *); @@ -108,37 +97,44 @@ static PyObject *py_singleton_factory_get_instance(PyObject *, PyObject *); * * ******************************************************************************/ -static void py_singleton_candidate_interface_init(GSingletonCandidateIface *iface, gpointer *unused) +static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *iface, gpointer *unused) { #define SINGLETON_CANDIDATE_DOC \ "The SingletonCandidate class is a required interface for objects" \ " aiming at becoming singleton instances. All shared singletons are" \ " registered within a pychrysalide.glibext.SingletonFactory object.\n" \ "\n" \ + "Implementations of the pychrysalide.glibext.HashableObject and" \ + " pychrysalide.glibext.ComparableObject interfaces are required for" \ + " types implementing the SingletonCandidate interface.\n" \ + "\n" \ "The main implemantations come with types derived from" \ - " pychrysalide.analysis.DataType.\n" \ + " pychrysalide.analysis.DataType (with possible recursivity) or from" \ + " pychrysalide.arch.ArchOperand.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, SingletonCandidate):\n" \ + " class NewImplem(GObject.Object, HashableObject, ComparableObject," \ + " SingletonCandidate):\n" \ " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ + "* pychrysalide.glibext.SingletonCandidate._mark_as_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._is_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._dup().\n" \ + "\n" \ + "The following methods may bbe defined for new implementations if" \ + " inner SingletonCandidate objets are carried:\n" \ "* pychrysalide.glibext.SingletonCandidate._list_inner_instances();\n" \ - "* pychrysalide.glibext.SingletonCandidate._update_inner_instances();\n"\ - "* pychrysalide.glibext.SingletonCandidate.__hash__();\n" \ - "* pychrysalide.glibext.SingletonCandidate.__eq__();\n" \ - "* pychrysalide.glibext.SingletonCandidate._set_read_only();\n" \ - "* pychrysalide.glibext.SingletonCandidate._is_read_only().\n" + "* pychrysalide.glibext.SingletonCandidate._update_inner_instances().\n" iface->update_inner = py_singleton_candidate_update_inner_instances_wrapper; iface->list_inner = py_singleton_candidate_list_inner_instances_wrapper; - iface->hash = py_singleton_candidate___hash__wrapper; - iface->is_equal = py_singleton_candidate___eq__wrapper; + iface->mark_as_ro = py_singleton_candidate_mark_as_read_only_wrapper; + iface->is_ro = py_singleton_candidate_is_read_only_wrapper; - iface->set_ro = py_singleton_candidate_set_ro_wrapper; - iface->is_ro = py_singleton_candidate_is_ro_wrapper; + iface->dup = py_singleton_candidate_dup_wrapper; } @@ -313,122 +309,97 @@ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCand * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *candidate) +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __hash__, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to produce a hash of the object.\n" \ - "\n" \ - "The result must be an integer value up to 64 bits." \ - "\n" \ - "Inner instances which are listed through the" \ - " pychrysalide.glibext.SingletonCandidate._list_inner_instances()" \ - " method do not need to get processed here as they are handled" \ - " automatically by the interface core." \ +#define SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _mark_as_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to seal the object as unmodifiable.\n" \ + "\n" \ + "No result is expected." \ ) - result = 0; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__hash__")) - { - pyret = run_python_method(pyobj, "__hash__", NULL); + pyret = run_python_method(pyobj, "_mark_as_read_only", NULL); - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLongMask(pyret); - - Py_DECREF(pyret); - - } - - } + Py_XDECREF(pyret); Py_DECREF(pyobj); PyGILState_Release(gstate); - return result; - } /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *candidate, const GSingletonCandidate *other) +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_EQ_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __eq__, "$self, other, /", \ - METH_NOARGS, \ - "Abstract method used to provide the *__eq__* method for" \ - " rich comparison.\n" \ - "\n" \ - "The expected result is a boolean value." \ +#define SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _is_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to provide the state of the object: are" \ + " its properties frozen (*True*) or can it be modified" \ + " (*False*)?\n" \ + "\n" \ + "The result has to be a boolean status.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a boolean." \ ) - result = 0; + result = false; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__eq__")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); - - pyret = run_python_method(pyobj, "__eq__", args); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLong(pyret); + pyret = run_python_method(pyobj, "_is_read_only", NULL); - Py_DECREF(pyret); - - } + if (pyret != NULL) + { + if (PyBool_Check(pyret)) + result = (pyret == Py_True); - Py_DECREF(args); + else + PyErr_SetString(PyExc_TypeError, _("status has to be provided as a boolean value")); } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -442,80 +413,38 @@ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate * * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Marque un candidat comme figé. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : - * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *candidate) +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *candidate) { + GSingletonCandidate *result; /* Instance à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ + PyObject *state; /* Validation du mode */ -#define SINGLETON_CANDIDATE_SET_RO_WRAPPER PYTHON_WRAPPER_DEF \ +#define SINGLETON_CANDIDATE_DUP_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _set_read_only, "$self, /", \ + _dup, "$self", \ METH_NOARGS, \ - "Abstract method used to mark the content of a singleton" \ - " candidate as read-only.\n" \ + "Abstract method used to create a copy of the object. This" \ + " has to be able to get modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ "\n" \ - "The read-only state is mandatory once the candidate is" \ - " registered inside a pychrysalide.glibext.SingletonFactory"\ - " instance as official singleton." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(candidate)); - - if (has_python_method(pyobj, "_set_read_only")) - { - pyret = run_python_method(pyobj, "_set_read_only", NULL); - - Py_XDECREF(pyret); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Indique si le candidat est figé. * -* * -* Retour : true si le contenu du candidat ne peut plus être modifié. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *candidate) -{ - bool result; /* Etat à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *pyret; /* Bilan de consultation */ - -#define SINGLETON_CANDIDATE_IS_RO_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _is_read_only, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to retrieve the status of the data" \ - " contained by a singleton candidate.\n" \ + "The result has to be a new intance of type(self).\n" \ + "\n" \ + "A *TypeError* exception is raised if the type of the" \ + " return value is different from the type of self.\n" \ "\n" \ - "The retured value is *True* if the candidate is" \ - " registered inside a pychrysalide.glibext.SingletonFactory"\ - " instance as official singleton, *False* otherwise." \ + "A *ValueError* exception is raised of the return object" \ + " is in read-only mode." \ ) result = false; @@ -524,16 +453,32 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "_is_read_only")) + pyret = run_python_method(pyobj, "_dup", NULL); + + if (pyret != NULL) { - pyret = run_python_method(pyobj, "_is_read_only", NULL); + if (Py_TYPE(pyret) != Py_TYPE(pyobj)) + PyErr_SetString(PyExc_TypeError, _("the result type is different from the source type")); - result = (pyret == Py_True); + else + { + state = py_singleton_candidate_is_read_only(pyret, NULL); - Py_XDECREF(pyret); + if (state != NULL) + { + if (state != Py_False) + PyErr_SetString(PyExc_ValueError, _("the result type can not be in read-only mode")); + + Py_DECREF(state); + + } + + } } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -545,97 +490,51 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand /****************************************************************************** * * -* Paramètres : self = objet dont l'instance se veut unique. * +* Paramètres : self = objet manipulé ici. * * args = adresse non utilisée ici. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args) +static PyObject *py_singleton_candidate_dup(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - guint hash; /* Valeur d'empreitne */ + GSingletonCandidate *copy; /* Copie des mécanismes natifs */ -#define SINGLETON_CANDIDATE_HASH_METHOD PYTHON_METHOD_DEF \ -( \ - hash, "$self", \ - METH_NOARGS, py_singleton_candidate, \ - "Compute the hash value of the singleton candidate.\n" \ - "\n" \ - "The method relies on the interface core to include in the" \ - " process the optional embedded instances which may become" \ - " singletons.\n" \ - "\n" \ - "The result is an integer value.\n" \ - "\n" \ - "Even if the Python *hash()* method, relying on the" \ - " pychrysalide.glibext.SingletonCandidate.__hash__()" \ - " implementation, provides values up to 64 bits, the final" \ - " hashes processed by the native GLib hash methods are" \ - " limited to 32 bits values." \ +#define SINGLETON_CANDIDATE_DUP_METHOD PYTHON_METHOD_DEF \ +( \ + dup, "$self", \ + METH_NOARGS, py_singleton_candidate, \ + "Create a copy of the object. This has to be able to get" \ + " modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ + "\n" \ + "The result has to be a new intance of type(self)." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - hash = g_singleton_candidate_hash(candidate); + copy = g_singleton_candidate_dup(candidate); - result = PyLong_FromUnsignedLong(hash); + if (copy == NULL) + result = NULL; - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * -* * -* Description : Fournit une liste de candidats embarqués par un candidat. * -* * -* Retour : Liste de candidats internes, vide si aucun. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GSingletonCandidate *candidate; /* Mécanismes natifs */ - size_t count; /* Quantité d'objets internes */ - GSingletonCandidate **instances; /* Liste des embarqués */ - size_t i; /* Boucle de parcours */ - -#define SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - inner_instances, py_singleton_candidate, \ - "List of optional internal singleton candidate instances.\n" \ - "\n" \ - "The result has to be a tuple containing zero or more" \ - " pychrysalide.glibext.SingletonCandidate instances." \ -) - - candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - - instances = g_singleton_candidate_list_inner_instances(candidate, &count); + else + { + result = pygobject_new(G_OBJECT(candidate)); - result = PyTuple_New(count); + unref_object(copy); - for (i = 0; i < count; i++) - { - PyTuple_SetItem(result, i, pygobject_new(G_OBJECT(instances[i]))); - g_object_unref(G_OBJECT(instances[i])); } - if (instances != NULL) - free(instances); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -649,87 +548,33 @@ static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void * * * Description : Indique si le candidat est figé. * * * -* Retour : true si le contenu du candidat ne peut plus être modifié. * +* Retour : True si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_get_read_only(PyObject *self, void *closure) +static PyObject *py_singleton_candidate_is_read_only(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - bool status; /* Etat de l'élément consulté */ + bool state; /* Etat de l'objet courant */ -#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - read_only, py_singleton_candidate, \ - "State of the singleton candidate content.\n" \ - "\n" \ - "The result is a boolean: *True* if the object is registered" \ - " as singleton, *False* otherwise.\n" \ - "\n" \ - "Once a singleton, the object must not change its content as" \ - " it is a shared instance." \ +#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + read_only, py_singleton_candidate, \ + "Boolean state of the object: *True* if all its properties are" \ + " frozen, *False* if the object can be modified." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - status = g_singleton_candidate_is_read_only(candidate); + state = g_singleton_candidate_is_read_only(candidate); - result = status ? Py_True : Py_False; + result = state ? Py_True : Py_False; Py_INCREF(result); - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * -* * -* Description : Effectue une comparaison avec un objet 'SingletonCandidate'. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, int op) -{ - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GSingletonCandidate *cand_a; /* Premier élément à traiter */ - GSingletonCandidate *cand_b; /* Second élément à traiter */ - gboolean status; /* Résultat d'une comparaison */ - - if (op != Py_EQ) - { - result = Py_NotImplemented; - goto cmp_done; - } - - ret = PyObject_IsInstance(b, (PyObject *)get_python_singleton_candidate_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - cand_a = G_SINGLETON_CANDIDATE(pygobject_get(a)); - cand_b = G_SINGLETON_CANDIDATE(pygobject_get(b)); - - status = g_singleton_candidate_is_equal(cand_a, cand_b); - - result = (status ? Py_True : Py_False); - - cmp_done: - - Py_INCREF(result); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -753,16 +598,14 @@ PyTypeObject *get_python_singleton_candidate_type(void) static PyMethodDef py_singleton_candidate_methods[] = { SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER, SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER, - SINGLETON_CANDIDATE_HASH_WRAPPER, - SINGLETON_CANDIDATE_EQ_WRAPPER, - SINGLETON_CANDIDATE_SET_RO_WRAPPER, - SINGLETON_CANDIDATE_IS_RO_WRAPPER, - SINGLETON_CANDIDATE_HASH_METHOD, + SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_DUP_WRAPPER, + SINGLETON_CANDIDATE_DUP_METHOD, { NULL } }; static PyGetSetDef py_singleton_candidate_getseters[] = { - SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB, SINGLETON_CANDIDATE_READ_ONLY_ATTRIB, { NULL } }; @@ -778,8 +621,6 @@ PyTypeObject *get_python_singleton_candidate_type(void) .tp_doc = SINGLETON_CANDIDATE_DOC, - .tp_richcompare = py_singleton_candidate_richcompare, - .tp_methods = py_singleton_candidate_methods, .tp_getset = py_singleton_candidate_getseters @@ -804,7 +645,7 @@ PyTypeObject *get_python_singleton_candidate_type(void) bool ensure_python_singleton_candidate_is_registered(void) { - PyTypeObject *type; /* Type Python 'BinContent' */ + PyTypeObject *type; /* Type 'SingletonCandidate' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ @@ -887,66 +728,6 @@ int convert_to_singleton_candidate(PyObject *arg, void *dst) /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_factory_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_singleton_factory_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_SINGLETON_FACTORY, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -973,11 +754,6 @@ static int py_singleton_factory_init(PyObject *self, PyObject *args, PyObject *k "\n" \ "The first processed instance defines the type handled by the factory." - /* Récupération des paramètres */ - - ret = PyArg_ParseTuple(args, ""); - if (!ret) return -1; - /* Initialisation d'un objet GLib */ ret = forward_pygobjet_init(self); diff --git a/plugins/pychrysalide/analysis/storage/storage.c b/plugins/pychrysalide/glibext/storage.c index c54fe0f..f2962bf 100644 --- a/plugins/pychrysalide/analysis/storage/storage.c +++ b/plugins/pychrysalide/glibext/storage.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * storage.c - équivalent Python du fichier "analysis/storage/storage.c" + * storage.c - équivalent Python du fichier "glibext/storage.c" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,25 +25,23 @@ #include "storage.h" +#include <assert.h> #include <pygobject.h> -#include <analysis/storage/storage-int.h> -#include <plugins/dt.h> +#include <glibext/storage-int.h> #include "serialize.h" -#include "../../access.h" -#include "../../helpers.h" -#include "../../common/packed.h" +#include "../access.h" +#include "../helpers.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_object_storage_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(object_storage, G_TYPE_OBJECT_STORAGE); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_object_storage_init(PyObject *, PyObject *, PyObject *); @@ -68,9 +66,6 @@ static PyObject *py_object_storage_unpack_object(PyObject *, PyObject *); /* Sauvegarde un object sous forme de données rassemblées. */ static PyObject *py_object_storage_store_object(PyObject *, PyObject *); -/* Sauvegarde un object interne sous forme de données. */ -static PyObject *py_object_storage_pack_object(PyObject *, PyObject *); - /* ---------------------------------------------------------------------------------- */ @@ -80,66 +75,6 @@ static PyObject *py_object_storage_pack_object(PyObject *, PyObject *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_object_storage_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_object_storage_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_OBJECT_STORAGE, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -154,7 +89,9 @@ static PyObject *py_object_storage_new(PyTypeObject *type, PyObject *args, PyObj static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds) { - const char *hash; /* Empreinte de contenu */ + const char *type; /* Type global de conservation */ + unsigned char version; /* Version de ce type */ + const char *uid; /* Identifiant de distinction */ int ret; /* Bilan de lecture des args. */ GObjectStorage *storage; /* Mécanismes natifs */ @@ -164,14 +101,15 @@ static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " ObjectStorage(hash)" \ + " ObjectStorage(type, version uid)" \ "\n" \ - "Where *hash* should a string built from the checksum of the" \ - " relative binary content linked to the storage.pychrysalide." + "Where *type* is a short string describing the storage kind," \ + " *version* provides a version control for this type and *uid* is" \ + " an arbitrary unique identifier used for creating temporary files." /* Récupération des paramètres */ - ret = PyArg_ParseTuple(args, "s", &hash); + ret = PyArg_ParseTuple(args, "sbs", &type, &version, &uid); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -183,7 +121,8 @@ static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds storage = G_OBJECT_STORAGE(pygobject_get(self)); - storage->hash = strdup(hash); + if (!g_object_storage_create(storage, type, version, uid)) + return -1; return 0; @@ -212,27 +151,27 @@ static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds static PyObject *py_object_storage_load(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + const char *filename; /* Fichier de source à traiter */ int ret; /* Bilan de lecture des args. */ GObjectStorage *storage; /* Mécanismes natifs */ -#define OBJECT_STORAGE_LOAD_METHOD PYTHON_METHOD_DEF \ -( \ - load, "pbuf, /", \ - METH_STATIC | METH_VARARGS, py_object_storage, \ - "Construct a new storage from a buffer.\n" \ - "\n" \ - "The *pbuf* has to be an instance of type" \ - " pychrysalide.common.PackedBuffer.\n" \ - "\n" \ - "The result is a new pychrysalide.analysis.storage.ObjectStorage" \ - " object on success, *None* otherwise." \ +#define OBJECT_STORAGE_LOAD_METHOD PYTHON_METHOD_DEF \ +( \ + load, "filename, /", \ + METH_STATIC | METH_VARARGS, py_object_storage, \ + "Construct a new storage from a filename.\n" \ + "\n" \ + "The *filename* argument points to the source file to" \ + " read.\n" \ + "\n" \ + "The result is a new pychrysalide.glibext.ObjectStorage" \ + " object on success, *None* otherwise." \ ) - ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "s", &filename); if (!ret) return NULL; - storage = g_object_storage_load(pbuf); + storage = g_object_storage_load(filename); if (storage == NULL) { @@ -242,7 +181,7 @@ static PyObject *py_object_storage_load(PyObject *self, PyObject *args) else { result = pygobject_new(G_OBJECT(storage)); - g_object_unref(G_OBJECT(storage)); + unref_object(storage); } return result; @@ -266,29 +205,29 @@ static PyObject *py_object_storage_load(PyObject *self, PyObject *args) static PyObject *py_object_storage_store(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + const char *filename; /* Fichier de destination */ int ret; /* Bilan de lecture des args. */ GObjectStorage *storage; /* Mécanismes natifs */ bool status; /* Bilan de l'opération */ #define OBJECT_STORAGE_STORE_METHOD PYTHON_METHOD_DEF \ ( \ - store, "$self, pbuf, /", \ + store, "$self, filename, /", \ METH_VARARGS, py_object_storage, \ - "Save a storage into a buffer.\n" \ + "Save a storage into a file.\n" \ "\n" \ - "The *pbuf* has to be an instance of type" \ - " pychrysalide.common.PackedBuffer.\n" \ + "The *filename* argument points to the destination" \ + " file to write.\n" \ "\n" \ "The result is *True* on success, *False* otherwise." \ ) - ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "s", &filename); if (!ret) return NULL; storage = G_OBJECT_STORAGE(pygobject_get(self)); - status = g_object_storage_store(storage, pbuf); + status = g_object_storage_store(storage, filename); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -331,7 +270,7 @@ static PyObject *py_object_storage_load_object(PyObject *self, PyObject *args) " the data to unserialize.\n" \ "\n" \ "The result is a pychrysalide.analysis.storage.SerializableObject" \ - " instancet in case of success, or None in case of failure." \ + " instancet in case of success, or *None* in case of failure." \ ) ret = PyArg_ParseTuple(args, "sK", &name, &pos); @@ -370,31 +309,34 @@ static PyObject *py_object_storage_load_object(PyObject *self, PyObject *args) static PyObject *py_object_storage_unpack_object(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ + int fd; /* Flux de fonnées courant */ const char *name; /* Désignation de groupe */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ int ret; /* Bilan de lecture des args. */ GObjectStorage *storage; /* Mécanismes natifs */ GSerializableObject *object; /* Objet reconstruit ou NULL */ #define OBJECT_STORAGE_UNPACK_OBJECT_METHOD PYTHON_METHOD_DEF \ ( \ - unpack_object, "$self, name, pbuf, /", \ + unpack_object, "$self, fd, name, /", \ METH_VARARGS, py_object_storage, \ - "Load an object from a buffer with a location pointing to data.\n" \ + "Load an object from a reference to serialized data.\n" \ "\n" \ - "The *name* is a string label for the group of target objects and" \ - " *pbuf* has to be a pychrysalide.common.PackedBuffer instance.\n" \ + "The *fd* argument is a file descriptor pointing to the data" \ + " stream for a current object being restored. A reference to" \ + " another object belonging to a group pointed by the string *name*" \ + " should be available at the current read position for this data" \ + " stream.\n" \ "\n" \ "The result is a pychrysalide.analysis.storage.SerializableObject" \ - " instancet in case of success, or None in case of failure." \ + " instancet in case of success, or *None* in case of failure." \ ) - ret = PyArg_ParseTuple(args, "sO&", &name, convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "is", &fd, &name); if (!ret) return NULL; storage = G_OBJECT_STORAGE(pygobject_get(self)); - object = g_object_storage_unpack_object(storage, name, pbuf); + object = g_object_storage_unpack_object(storage, fd, name); if (object != NULL) result = pygobject_new(G_OBJECT(object)); @@ -443,7 +385,7 @@ static PyObject *py_object_storage_store_object(PyObject *self, PyObject *args) " pychrysalide.analysis.storage.SerializableObject instance.\n" \ "\n" \ "The result is the position of the data for stored object," \ - " provided as an integer offset, in case of success or None" \ + " provided as an integer offset, in case of success or *None*" \ " in case of failure." \ ) @@ -469,62 +411,6 @@ static PyObject *py_object_storage_store_object(PyObject *self, PyObject *args) /****************************************************************************** * * -* Paramètres : self = classe représentant une mémorisation de types. * -* args = arguments fournis à l'appel. * -* * -* Description : Sauvegarde un object interne sous forme de données. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_object_storage_pack_object(PyObject *self, PyObject *args) -{ - PyObject *result; /* Emplacement à retourner */ - const char *name; /* Désignation de groupe */ - GSerializableObject *object; /* Objet à traiter */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ - int ret; /* Bilan de lecture des args. */ - GObjectStorage *storage; /* Mécanismes natifs */ - bool status; /* Bilan de l'opération */ - -#define OBJECT_STORAGE_PACK_OBJECT_METHOD PYTHON_METHOD_DEF \ -( \ - pack_object, "$self, name, object, pbuf/", \ - METH_VARARGS, py_object_storage, \ - "Save an object as serialized data and store the location of" \ - " the data intro a buffer.\n" \ - "\n" \ - "The *name* is a string label for the group of target objects," \ - " the processed *object* has to be a" \ - " pychrysalide.analysis.storage.SerializableObject instance" \ - " and *pbuf* is expected to be a" \ - " pychrysalide.common.PackedBuffer instance.\n" \ - "\n" \ - "The status of the operation is returned as a boolean value:" \ - " *True* for success, *False* for failure." \ -) - - ret = PyArg_ParseTuple(args, "sO&O&", &name, convert_to_serializable_object, &object, - convert_to_packed_buffer, &pbuf); - if (!ret) return NULL; - - storage = G_OBJECT_STORAGE(pygobject_get(self)); - - status = g_object_storage_pack_object(storage, name, object, pbuf); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : - * * * * Description : Fournit un accès à une définition de type à diffuser. * @@ -543,7 +429,6 @@ PyTypeObject *get_python_object_storage_type(void) OBJECT_STORAGE_LOAD_OBJECT_METHOD, OBJECT_STORAGE_UNPACK_OBJECT_METHOD, OBJECT_STORAGE_STORE_OBJECT_METHOD, - OBJECT_STORAGE_PACK_OBJECT_METHOD, { NULL } }; @@ -555,7 +440,7 @@ PyTypeObject *get_python_object_storage_type(void) PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.analysis.storage.ObjectStorage", + .tp_name = "pychrysalide.glibext.ObjectStorage", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT, @@ -579,7 +464,7 @@ PyTypeObject *get_python_object_storage_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide....ObjectStorage'. * +* Description : Prend en charge l'objet 'pychrysalide.glibext.ObjectStorage'.* * * * Retour : Bilan de l'opération. * * * @@ -597,7 +482,7 @@ bool ensure_python_object_storage_is_registered(void) if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { - module = get_access_to_python_module("pychrysalide.analysis.storage"); + module = get_access_to_python_module("pychrysalide.glibext"); dict = PyModule_GetDict(module); diff --git a/plugins/pychrysalide/analysis/storage/storage.h b/plugins/pychrysalide/glibext/storage.h index a0a2c18..681f99a 100644 --- a/plugins/pychrysalide/analysis/storage/storage.h +++ b/plugins/pychrysalide/glibext/storage.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * storage.h - prototypes pour l'équivalent Python du fichier "analysis/storage/storage.h" + * storage.h - prototypes pour l'équivalent Python du fichier "glibext/storage.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_STORAGE_H -#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_STORAGE_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H #include <Python.h> @@ -34,7 +34,7 @@ /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_object_storage_type(void); -/* Prend en charge l'objet 'pychrysalide.analysis.storage.ObjectStorage'. */ +/* Prend en charge l'objet 'pychrysalide.glibext.ObjectStorage'. */ bool ensure_python_object_storage_is_registered(void); /* Tente de convertir en conservateur d'objets. */ @@ -45,4 +45,4 @@ int convert_to_object_storage_or_none(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_STORAGE_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H */ diff --git a/plugins/pychrysalide/glibext/strbuilder.c b/plugins/pychrysalide/glibext/strbuilder.c new file mode 100644 index 0000000..a6de0f0 --- /dev/null +++ b/plugins/pychrysalide/glibext/strbuilder.c @@ -0,0 +1,542 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.c - équivalent Python du fichier "glibext/strbuilder.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "strbuilder.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/strbuilder-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void py_string_builder_interface_init(GStringBuilderInterface *, gpointer *); + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static bool py_string_builder_to_string_wrapper(const GStringBuilder *, unsigned int, sized_binary_t *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet la description d'un objet définie par son parent. */ +static PyObject *py_string_builder_parent_to_string(PyObject *, PyObject *); + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static PyObject *py_string_builder_to_string(PyObject *, PyObject *); + +/* Fournit une représentation de l'objet exportable. */ +static PyObject *py_string_builder_str(PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_string_builder_interface_init(GStringBuilderInterface *iface, gpointer *unused) +{ +#define STRING_BUILDER_DOC \ + "The StringBuilder class defines a interface for native objects aiming" \ + " at providing a string representation of themselves.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, StringBuilder):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.StringBuilder._to_string().\n" + + iface->to_string = py_string_builder_to_string_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * +* * +******************************************************************************/ + +static bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + const char *utf8; /* Chaîne UTF-8 portée */ + Py_ssize_t size; /* Taille de ces données */ + +#define STRING_BUILDER_TO_STRING_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _to_string, "$self, /, flags=0", \ + METH_VARARGS, \ + "Abstract method providing a string representation for the" \ + " object which is used as the default implementation of the" \ + " __repr__() method.\n" \ + "\n" \ + "The optional *flags* argument define hints for the operation" \ + " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ + "\n" \ + "The result has to be a string." \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a string." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(builder)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(flags)); + + pyret = run_python_method(pyobj, "_to_string", args); + + if (pyret != NULL) + { + if (PyUnicode_Check(pyret)) + { + utf8 = PyUnicode_AsUTF8AndSize(pyret, &size); + + if (utf8 != NULL) + { + assert(size >= 0); + + add_to_sized_binary(out, utf8, size); + result = true; + + } + + } + + if (!result) + PyErr_SetString(PyExc_TypeError, _("object description has to get provided as an UTF-8 string value")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet la description d'un objet définie par son parent. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_parent_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + GStringBuilderInterface *iface; /* Interface utilisée */ + GStringBuilderInterface *parent_iface; /* Interface parente */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_PARENT_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + parent_to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.StringBuilder" \ + " interface." \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._to_string() function has to be" \ + " used instead.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + iface = G_STRING_BUILDER_GET_IFACE(builder); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the StringBuilder interface")); + + result = NULL; + + } + else + { + init_sized_binary(&out); + + status = parent_iface->to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation for the object which is used" \ + " as the default implementation of the __repr__() method.\n" \ + "\n" \ + "The optional *flags* argument define hints for the operation" \ + " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + init_sized_binary(&out); + + status = g_string_builder_to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Fournit une représentation de l'objet exportable. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_str(PyObject *self) +{ + PyObject *result; /* Emplacement à retourner */ + GStringBuilder *builder; /* Mécanismes natifs */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + + builder = G_STRING_BUILDER(pygobject_get(self)); + + init_sized_binary(&out); + + status = g_string_builder_to_string(builder, 0, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_string_builder_type(void) +{ + static PyMethodDef py_string_builder_methods[] = { + STRING_BUILDER_TO_STRING_WRAPPER, + STRING_BUILDER_PARENT_TO_STRING_METHOD, + STRING_BUILDER_TO_STRING_METHOD, + { NULL } + }; + + static PyGetSetDef py_string_builder_getseters[] = { + { NULL } + }; + + static PyTypeObject py_string_builder_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.StringBuilder", + .tp_basicsize = sizeof(PyObject), + + .tp_str = py_string_builder_str, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = STRING_BUILDER_DOC, + + .tp_methods = py_string_builder_methods, + .tp_getset = py_string_builder_getseters + + }; + + return &py_string_builder_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_string_builder_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'StringBuilder' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_string_builder_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_string_builder_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_STRING_BUILDER, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'exportation en chaîne. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_string_builder(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_string_builder_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to string builder"); + break; + + case 1: + *((GStringBuilder **)dst) = G_STRING_BUILDER(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/strbuilder.h b/plugins/pychrysalide/glibext/strbuilder.h new file mode 100644 index 0000000..1881cae --- /dev/null +++ b/plugins/pychrysalide/glibext/strbuilder.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.h - prototypes pour l'équivalent Python du fichier "glibext/strbuilder.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_string_builder_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'. */ +bool ensure_python_string_builder_is_registered(void); + +/* Tente de convertir en interface d'exportation en chaîne. */ +int convert_to_string_builder(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H */ diff --git a/plugins/pychrysalide/analysis/storage/tpmem.c b/plugins/pychrysalide/glibext/tpmem.c index ae07008..ae07008 100644 --- a/plugins/pychrysalide/analysis/storage/tpmem.c +++ b/plugins/pychrysalide/glibext/tpmem.c diff --git a/plugins/pychrysalide/analysis/storage/tpmem.h b/plugins/pychrysalide/glibext/tpmem.h index 1085632..1085632 100644 --- a/plugins/pychrysalide/analysis/storage/tpmem.h +++ b/plugins/pychrysalide/glibext/tpmem.h diff --git a/plugins/pychrysalide/glibext/comparison.c b/plugins/pychrysalide/glibext/work.c index 548f700..e6791e3 100644 --- a/plugins/pychrysalide/glibext/comparison.c +++ b/plugins/pychrysalide/glibext/work.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - équivalent Python du fichier "glibext/comparison.h" + * work.c - prototypes pour l'équivalent Python du fichier "glibext/work.c" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,38 +22,42 @@ */ -#include "comparison.h" +#include "work.h" +#include <assert.h> #include <pygobject.h> -#include <glibext/comparison-int.h> +#include <glibext/work-int.h> -#include "constants.h" #include "../access.h" #include "../helpers.h" -#include "../analysis/content.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Procède à l'initialisation de l'interface de comparaison. */ -static void py_comparable_item_interface_init(GComparableItemIface *, gpointer *); +/* Initialise la classe des travaux programmés. */ +static int py_generic_work_init_gclass(GGenericWorkClass *, PyTypeObject *); -/* Réalise une comparaison entre objets selon un critère précis. */ -static bool py_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_generic_work_init(PyObject *, PyObject *, PyObject *); + +/* Mène l'opération programmée. */ +static void py_generic_work_run_wrapper(GGenericWork *); /* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ -/* Effectue une comparaison avec un objet 'ComparableItem'. */ -static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); +/* Mène l'opération programmée. */ +static PyObject *py_generic_work_process(PyObject *, PyObject *); @@ -64,88 +68,111 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); /****************************************************************************** * * -* Paramètres : iface = interface GLib à initialiser. * -* unused = adresse non utilisée ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * -* Description : Procède à l'initialisation de l'interface de comparaison. * +* Description : Initialise la classe des travaux programmés. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_comparable_item_interface_init(GComparableItemIface *iface, gpointer *unused) +static int py_generic_work_init_gclass(GGenericWorkClass *gclass, PyTypeObject *pyclass) { + PY_CLASS_SET_WRAPPER(gclass->run, py_generic_work_run_wrapper); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_generic_work_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ -#define COMPARABLE_ITEM_DOC \ - "ComparableItem provides an interface to compare objects.\n" \ - "\n" \ - "A typical class declaration for a new implementation looks like:\n" \ - "\n" \ - " class NewImplem(GObject.Object, ComparableItem):\n" \ - " ...\n" \ - "\n" +#define GENERIC_WORK_DOC \ + "GenericWork defines a basic work aimed to get processed in a" \ + " thread set up by a pychrysalide.glibext.WorkQueue object.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " GenericWork()" \ + "\n" \ + "The following method has to be defined for new classes:\n" \ + "* pychrysalide.glibext.GenericWork._run();\n" \ + "\n" \ + "The following signal may be emitted:\n" \ + "* *work-completed* : when the work has been processed." - iface->cmp_rich = py_comparable_item_compare_rich; + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; } /****************************************************************************** * * -* Paramètres : item = premier objet à cnsulter pour une comparaison. * -* other = second objet à cnsulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * +* Paramètres : work = travail à effectuer. * * * -* Description : Réalise une comparaison entre objets selon un critère précis.* +* Description : Mène l'opération programmée. * * * -* Retour : true si la comparaison a pu être effectuée, false sinon. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool py_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) +static void py_generic_work_run_wrapper(GGenericWork *work) { - bool result; /* Etat à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyitem; /* Objet Python concerné #1 */ - PyObject *pyother; /* Objet Python concerné #2 */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ - int ret; /* Bilan d'une conversion */ - result = false; +#define GENERIC_WORK_RUN_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _run, "$self", \ + METH_NOARGS, \ + "Abstract method used to process the work.\n" \ + "\n" \ + "No result is expected from this function." \ +) gstate = PyGILState_Ensure(); - pyitem = pygobject_new(G_OBJECT(item)); - pyother = pygobject_new(G_OBJECT(other)); + pyobj = pygobject_new(G_OBJECT(work)); - pyret = PyObject_RichCompare(pyitem, pyother, op); - - if (pyret != NULL) + if (has_python_method(pyobj, "_run")) { - ret = PyBool_Check(pyret); - - if (ret) - { - *status = (pyret == Py_True); - result = true; - } + pyret = run_python_method(pyobj, "_run", NULL); - Py_DECREF(pyret); + Py_XDECREF(pyret); } - Py_DECREF(pyother); - Py_DECREF(pyitem); + Py_DECREF(pyobj); PyGILState_Release(gstate); - return result; - } @@ -157,46 +184,39 @@ static bool py_comparable_item_compare_rich(const GComparableItem *item, const G /****************************************************************************** * * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * * * -* Description : Effectue une comparaison avec un objet 'ComparableItem'. * +* Description : Mène l'opération programmée. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op) +static PyObject *py_generic_work_process(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GComparableItem *item_a; /* Instance à manipuler #1 */ - GComparableItem *item_b; /* Instance à manipuler #2 */ - bool valid; /* Indication de validité */ - bool status; /* Résultat d'une comparaison */ + GGenericWork *work; /* Version native */ - ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_item_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - item_a = G_COMPARABLE_ITEM(pygobject_get(a)); - item_b = G_COMPARABLE_ITEM(pygobject_get(b)); +#define GENERIC_WORK_PROCESS_METHOD PYTHON_METHOD_DEF \ +( \ + process, "$self", \ + METH_NOARGS, py_generic_work, \ + "Process the work.\n" \ + "\n" \ + "The function does not return anything.\n" \ + "\n" \ + "The *work-completed* signal is emitted when the" \ + " function terminates." \ +) - valid = g_comparable_item_compare_rich(item_a, item_b, op, &status); + work = G_GENERIC_WORK(pygobject_get(self)); - if (valid) - result = status ? Py_True : Py_False; - else - result = Py_NotImplemented; - - cmp_done: + g_generic_work_process(work); + result = Py_None; Py_INCREF(result); return result; @@ -216,35 +236,38 @@ static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op * * ******************************************************************************/ -PyTypeObject *get_python_comparable_item_type(void) +PyTypeObject *get_python_generic_work_type(void) { - static PyMethodDef py_comparable_item_methods[] = { + static PyMethodDef py_generic_work_methods[] = { + GENERIC_WORK_RUN_WRAPPER, + GENERIC_WORK_PROCESS_METHOD, { NULL } }; - static PyGetSetDef py_comparable_item_getseters[] = { + static PyGetSetDef py_generic_work_getseters[] = { { NULL } }; - static PyTypeObject py_comparable_item_type = { + static PyTypeObject py_generic_work_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.ComparableItem", - .tp_basicsize = sizeof(PyObject), + .tp_name = "pychrysalide.glibext.GenericWork", + .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = COMPARABLE_ITEM_DOC, + .tp_doc = GENERIC_WORK_DOC, - .tp_richcompare = py_comparable_item_richcompare, + .tp_methods = py_generic_work_methods, + .tp_getset = py_generic_work_getseters, - .tp_methods = py_comparable_item_methods, - .tp_getset = py_comparable_item_getseters, + .tp_init = py_generic_work_init, + .tp_new = py_generic_work_new, }; - return &py_comparable_item_type; + return &py_generic_work_type; } @@ -253,7 +276,7 @@ PyTypeObject *get_python_comparable_item_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.....ComparableItem'. * +* Description : Prend en charge l'objet 'pychrysalide.glibext.GenericWork'. * * * * Retour : Bilan de l'opération. * * * @@ -261,21 +284,13 @@ PyTypeObject *get_python_comparable_item_type(void) * * ******************************************************************************/ -bool ensure_python_comparable_item_is_registered(void) +bool ensure_python_generic_work_is_registered(void) { - PyTypeObject *type; /* Type Python 'ComparableItem' */ + PyTypeObject *type; /* Type Python 'GenericWork' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ - static GInterfaceInfo info = { /* Paramètres d'inscription */ - - .interface_init = (GInterfaceInitFunc)py_comparable_item_interface_init, - .interface_finalize = NULL, - .interface_data = NULL, - - }; - - type = get_python_comparable_item_type(); + type = get_python_generic_work_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -283,10 +298,9 @@ bool ensure_python_comparable_item_is_registered(void) dict = PyModule_GetDict(module); - if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_ITEM, type, &info)) - return false; + pyg_register_class_init(G_TYPE_GENERIC_WORK, (PyGClassInitFunc)py_generic_work_init_gclass); - if (!define_comparable_item_constants(type)) + if (!register_class_for_pygobject(dict, G_TYPE_GENERIC_WORK, type)) return false; } @@ -301,7 +315,7 @@ bool ensure_python_comparable_item_is_registered(void) * Paramètres : arg = argument quelconque à tenter de convertir. * * dst = destination des valeurs récupérées en cas de succès. * * * -* Description : Tente de convertir en élément comparable. * +* Description : Tente de convertir en travail générique. * * * * Retour : Bilan de l'opération, voire indications supplémentaires. * * * @@ -309,11 +323,11 @@ bool ensure_python_comparable_item_is_registered(void) * * ******************************************************************************/ -int convert_to_comparable_item(PyObject *arg, void *dst) +int convert_to_generic_work(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_item_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_generic_work_type()); switch (result) { @@ -323,11 +337,11 @@ int convert_to_comparable_item(PyObject *arg, void *dst) break; case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable item"); + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to generic work"); break; case 1: - *((GComparableItem **)dst) = G_COMPARABLE_ITEM(pygobject_get(arg)); + *((GGenericWork **)dst) = G_GENERIC_WORK(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/work.h b/plugins/pychrysalide/glibext/work.h new file mode 100644 index 0000000..de9a936 --- /dev/null +++ b/plugins/pychrysalide/glibext/work.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * work.h - prototypes pour l'équivalent Python du fichier "glibext/work.h" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_generic_work_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.GenericWork'. */ +bool ensure_python_generic_work_is_registered(void); + +/* Tente de convertir en travail générique. */ +int convert_to_generic_work(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H */ diff --git a/plugins/pychrysalide/glibext/workqueue.c b/plugins/pychrysalide/glibext/workqueue.c new file mode 100644 index 0000000..ca6c73c --- /dev/null +++ b/plugins/pychrysalide/glibext/workqueue.c @@ -0,0 +1,586 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * workqueue.c - prototypes pour l'équivalent Python du fichier "glibext/workqueue.c" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "workqueue.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/workqueue-int.h> + + +#include "work.h" +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(work_queue, G_TYPE_WORK_QUEUE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_work_queue_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Constitue un nouveau groupe de travail. */ +static PyObject *py_work_queue_define_group(PyObject *, PyObject *); + +/* Dissout un groupe de travail existant. */ +static PyObject *py_work_queue_delete_group(PyObject *, PyObject *); + +/* Place une nouvelle tâche en attente. */ +static PyObject *py_work_queue_schedule(PyObject *, PyObject *); + +/* Détermine si un groupe est vide de toute programmation. */ +static PyObject *py_work_queue_is_empty(PyObject *, PyObject *); + +/* Attend que toutes les tâches d'un groupe soient traitées. */ +static PyObject *py_work_queue_wait_for_completion(PyObject *, PyObject *); + +/* Force un réveil d'une attente en cours pour la confirmer. */ +static PyObject *py_work_queue_wake_up_waiters(PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_work_queue_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define WORK_QUEUE_DOC \ + "WorkQueue creates threads in order to process" \ + " pychrysalide.glibext.Work instances.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " WorkQueue()" + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Constitue un nouveau groupe de travail. * +* * +* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_define_group(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int count; /* Nombre de threads à activer */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + wgroup_id_t id; /* Identifiant de groupe créé */ + +#define WORK_QUEUE_DEFINE_GROUP_METHOD PYTHON_METHOD_DEF \ +( \ + define_group, "$self, /, count=0", \ + METH_VARARGS, py_work_queue, \ + "Create a new work group for a work queue.\n" \ + "\n" \ + "The *count* argument states the number of threads which will" \ + " be activated for the processing. If the provided value is" \ + " zero, then a default value based on the current hardware is" \ + " selected.\n" \ + "\n" \ + "The result is a work group identifier." \ +) + + count = 0; + + ret = PyArg_ParseTuple(args, "|I", &count); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + id = g_work_queue_define_group(queue, count); + + result = PyLong_FromUnsignedLong(id); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Dissout un groupe de travail existant. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_delete_group(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + +#define WORK_QUEUE_DELETE_GROUP_METHOD PYTHON_METHOD_DEF \ +( \ + delete_group, "$self, id", \ + METH_VARARGS, py_work_queue, \ + "Destroy a work group previously registered inside a queue.\n" \ + "\n" \ + "The *id* argument refers to a work group identifier. It" \ + " should have been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \ +) + + ret = PyArg_ParseTuple(args, "K", &id); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + g_work_queue_delete_group(queue, id); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Place une nouvelle tâche en attente. * +* * +* Retour : Bilan, qui correspond à l'existence du groupe ciblé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_schedule(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GGenericWork *work; /* Tâche à programmer */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + bool status; /* Bilan de programmation */ + +#define WORK_QUEUE_SCHEDULE_METHOD PYTHON_METHOD_DEF \ +( \ + schedule, "$self, work, id", \ + METH_VARARGS, py_work_queue, \ + "Schedule a work into the queue processing.\n" \ + "\n" \ + "The *work* argument has to point to a" \ + " pychrysalide.glibext.GenericWork instance, and the *id*" \ + " argument refers to a work group identifier which should have" \ + " been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group().\n"\ + "\n" \ + "The result is a boolean value: *True* in case of succes (does" \ + " the provided identifier exist?), *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "O&K", convert_to_generic_work, &work, &id); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + status = g_work_queue_schedule(queue, work, id); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Détermine si un groupe est vide de toute programmation. * +* * +* Retour : Etat du groupe de travail. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_is_empty(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + bool status; /* Bilan de consultation */ + +#define WORK_QUEUE_IS_EMPTY_METHOD PYTHON_METHOD_DEF \ +( \ + is_empty, "$self, id", \ + METH_VARARGS, py_work_queue, \ + "Signal if there is some remaining work to get processed by" \ + " the queue or not.\n" \ + "\n" \ + "The *id* argument refers to a work group identifier. It" \ + " should have been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group().\n"\ + "\n" \ + "The result is a boolean value: *True* if the group does not" \ + " contain any works or does not exist, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "K", &id); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + status = g_work_queue_is_empty(queue, id); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Attend que toutes les tâches d'un groupe soient traitées. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_wait_for_completion(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long rel; /* Durée d'attente */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + bool status; /* Bilan de l'attente menée */ + + /** + * Python appelle cette fonction, lorsqu'elle est sollicitée dans des scripts, + * via la fonction PyObject_Vectorcall(), qui obtient la structure de suivi + * du thread courant avec _PyThreadState_GET(). + * + * Un commentaire associé à cette dernière fonction précise : + * + * The caller must hold the GIL. + * + * Comme le verrou GIL est posé par Python au moment de l'exécution de + * py_work_queue_wait_for_completion(), aucune attente bloquante ne peut + * alors être engagée ici : elle bloquerait la tâche _run() d'un travail + * réalisé en Python(), comme son déférencement via unref_ojbect() qui + * implique une exécution de la fonction pyg_toggle_notify() appelant + * PyGILState_Ensure() dans python3-gi. + * + * Aussi une attente bornée dans le temps est la seule option possible + * ici. + */ + +#define WORK_QUEUE_WAIT_FOR_COMPLETION_METHOD PYTHON_METHOD_DEF \ +( \ + wait_for_completion, "$self, id, /, rel=100000", \ + METH_VARARGS, py_work_queue, \ + "Wait until all the works from a given group have been" \ + " processed.\n" \ + "\n" \ + "The *id* argument refers to a work group identifier. It" \ + " should have been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \ + " The *rel* range provide a relative time to wait (100ms by" \ + " default).\n" \ + "\n" \ + "The returned value is a boolean status: *False* on a timeout," \ + " *True* otherwise." \ + "\n" \ + "_Important note:_\n" \ + "As this function is called by Python with its Global" \ + " Interpreter Lock (GIL) held, an endless wait is impossible" \ + " because it would keep the GIL unreleased whereas others" \ + " threads (including those which may end the wait) may need to" \ + " acquire it.\n" \ + "\n" \ + "So an unoptimized timed wait is performed here instead, and" \ + " the returned status has to be checked by the caller in order" \ + " to schedule another wait if needed." \ +) + + rel = 100 * G_TIME_SPAN_MILLISECOND; + + ret = PyArg_ParseTuple(args, "K|K", &id, &rel); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + status = g_work_queue_wait_timed_for_completion(queue, id, rel); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Force un réveil d'une attente en cours pour la confirmer. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_wake_up_waiters(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + +#define WORK_QUEUE_WAKE_UP_WAITERS_METHOD PYTHON_METHOD_DEF \ +( \ + wake_up_waiters, "$self, id", \ + METH_VARARGS, py_work_queue, \ + "Force a wake up of all threads waiting for processing a given" \ + " queue work group.\n" \ + "\n" \ + "The *id* argument refers to a work group identifier. It" \ + " should have been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \ +) + + ret = PyArg_ParseTuple(args, "K", &id); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + g_work_queue_wake_up_waiters(queue, id); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_work_queue_type(void) +{ + static PyMethodDef py_work_queue_methods[] = { + WORK_QUEUE_DEFINE_GROUP_METHOD, + WORK_QUEUE_DELETE_GROUP_METHOD, + WORK_QUEUE_SCHEDULE_METHOD, + WORK_QUEUE_IS_EMPTY_METHOD, + WORK_QUEUE_WAIT_FOR_COMPLETION_METHOD, + WORK_QUEUE_WAKE_UP_WAITERS_METHOD, + { NULL } + }; + + static PyGetSetDef py_work_queue_getseters[] = { + { NULL } + }; + + static PyTypeObject py_work_queue_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.WorkQueue", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = WORK_QUEUE_DOC, + + .tp_methods = py_work_queue_methods, + .tp_getset = py_work_queue_getseters, + + .tp_init = py_work_queue_init, + .tp_new = py_work_queue_new, + + }; + + return &py_work_queue_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.WorkQueue'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_work_queue_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'WorkQueue' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_work_queue_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_WORK_QUEUE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en gestionnaire de travaux différés. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_work_queue(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_work_queue_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to work queue"); + break; + + case 1: + *((GWorkQueue **)dst) = G_WORK_QUEUE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/workqueue.h b/plugins/pychrysalide/glibext/workqueue.h new file mode 100644 index 0000000..3fe426b --- /dev/null +++ b/plugins/pychrysalide/glibext/workqueue.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * workqueue.h - prototypes pour l'équivalent Python du fichier "glibext/workqueue.h" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_work_queue_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.WorkQueue'. */ +bool ensure_python_work_queue_is_registered(void); + +/* Tente de convertir en gestionnaire de travaux différés. */ +int convert_to_work_queue(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H */ diff --git a/plugins/pychrysalide/gtkext/Makefile.am b/plugins/pychrysalide/gtkext/Makefile.am index 2e1260f..1d91751 100644 --- a/plugins/pychrysalide/gtkext/Makefile.am +++ b/plugins/pychrysalide/gtkext/Makefile.am @@ -1,19 +1,23 @@ noinst_LTLIBRARIES = libpychrysagtkext.la +# libpychrysagtkext_la_SOURCES = \ +# blockdisplay.h blockdisplay.c \ +# bufferdisplay.h bufferdisplay.c \ +# displaypanel.h displaypanel.c \ +# dockable.h dockable.c \ +# easygtk.h easygtk.c \ +# module.h module.c \ +# named.h named.c + libpychrysagtkext_la_SOURCES = \ - blockdisplay.h blockdisplay.c \ - bufferdisplay.h bufferdisplay.c \ - displaypanel.h displaypanel.c \ - dockable.h dockable.c \ - easygtk.h easygtk.c \ - module.h module.c \ - named.h named.c - -libpychrysagtkext_la_LIBADD = \ - graph/libpychrysagtkextgraph.la - -libpychrysagtkext_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + panel.h panel.c \ + module.h module.c + +# libpychrysagtkext_la_LIBADD = \ +# graph/libpychrysagtkextgraph.la + +libpychrysagtkext_la_CFLAGS = $(LIBGTK4_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT @@ -22,4 +26,4 @@ devdir = $(includedir)/chrysalide/$(subdir) dev_HEADERS = $(libpychrysagtkext_la_SOURCES:%c=) -SUBDIRS = graph +#SUBDIRS = graph diff --git a/plugins/pychrysalide/gtkext/module.c b/plugins/pychrysalide/gtkext/module.c index f8264af..fa59b2b 100644 --- a/plugins/pychrysalide/gtkext/module.c +++ b/plugins/pychrysalide/gtkext/module.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * module.c - intégration du répertoire gtkext en tant que module * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,13 +28,13 @@ #include <assert.h> -#include "blockdisplay.h" -#include "bufferdisplay.h" -#include "displaypanel.h" -#include "dockable.h" -#include "easygtk.h" -#include "named.h" -#include "graph/module.h" +//#include "blockdisplay.h" +//#include "bufferdisplay.h" +//#include "displaypanel.h" +//#include "dockable.h" +//#include "named.h" +#include "panel.h" +//#include "graph/module.h" #include "../helpers.h" @@ -71,7 +71,7 @@ bool add_gtkext_module(PyObject *super) result = (module != NULL); - if (result) result = add_gtkext_graph_module(module); + //if (result) result = add_gtkext_graph_module(module); if (!result) Py_XDECREF(module); @@ -99,14 +99,16 @@ bool populate_gtkext_module(void) result = true; - if (result) result = ensure_python_block_display_is_registered(); - if (result) result = ensure_python_buffer_display_is_registered(); - if (result) result = ensure_python_display_panel_is_registered(); - if (result) result = ensure_python_dockable_is_registered(); - if (result) result = ensure_python_easygtk_is_registered(); - if (result) result = ensure_python_built_named_widget_is_registered(); + if (result) result = ensure_python_tiled_panel_is_registered(); - if (result) result = populate_gtkext_graph_module(); + //if (result) result = ensure_python_block_display_is_registered(); + //if (result) result = ensure_python_buffer_display_is_registered(); + //if (result) result = ensure_python_display_panel_is_registered(); + //if (result) result = ensure_python_dockable_is_registered(); + //if (result) result = ensure_python_easygtk_is_registered(); + //if (result) result = ensure_python_built_named_widget_is_registered(); + + //if (result) result = populate_gtkext_graph_module(); assert(result); diff --git a/plugins/pychrysalide/gtkext/panel.c b/plugins/pychrysalide/gtkext/panel.c new file mode 100644 index 0000000..9f6589a --- /dev/null +++ b/plugins/pychrysalide/gtkext/panel.c @@ -0,0 +1,130 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel.c - prototypes pour l'équivalent Python du fichier "gtkext/panel.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "panel.h" + + +#include <pygobject.h> + + +#include <gtkext/panel.h> + + +#include "../access.h" +#include "../helpers.h" +#include "../helpers-ui.h" + + + +#define TILED_PANEL_DOC \ + "The TiledPanel class defines a panel widget for the framework main" \ + " window." + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_tiled_panel_type(void) +{ + static PyMethodDef py_tiled_panel_methods[] = { + { NULL } + }; + + static PyGetSetDef py_tiled_panel_getseters[] = { + { NULL } + }; + + static PyTypeObject py_tiled_panel_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.gtkext.TiledPanel", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = TILED_PANEL_DOC, + + .tp_methods = py_tiled_panel_methods, + .tp_getset = py_tiled_panel_getseters, + + }; + + static PyTypeObject *result = NULL; + + if (result == NULL) + result = define_python_dynamic_type(&py_tiled_panel_type); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.gtkext.TiledPanel'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_tiled_panel_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'TiledPanel' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_tiled_panel_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.gtkext"); + + dict = PyModule_GetDict(module); + + if (!ensure_gtk_widget_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, GTK_TYPE_TILED_PANEL, type)) + return false; + + } + + return true; + +} diff --git a/plugins/pychrysalide/gtkext/panel.h b/plugins/pychrysalide/gtkext/panel.h new file mode 100644 index 0000000..c5cbe86 --- /dev/null +++ b/plugins/pychrysalide/gtkext/panel.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel.h - prototypes pour l'équivalent Python du fichier "gtkext/panel.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GTKEXT_PANEL_H +#define _PLUGINS_PYCHRYSALIDE_GTKEXT_PANEL_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_tiled_panel_type(void); + +/* Prend en charge l'objet 'pychrysalide.gtkext.TiledPanel'. */ +bool ensure_python_tiled_panel_is_registered(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GTKEXT_PANEL_H */ diff --git a/plugins/pychrysalide/helpers-ui.c b/plugins/pychrysalide/helpers-ui.c new file mode 100644 index 0000000..982e676 --- /dev/null +++ b/plugins/pychrysalide/helpers-ui.c @@ -0,0 +1,141 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers-ui.c - simplification des interactions UI de base avec Python + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "helpers-ui.h" + + +#include <assert.h> +#include <pygobject.h> +#include <gtk/gtk.h> + + +#include "bindings.h" + + + +/* ---------------------------------------------------------------------------------- */ +/* CONFORTS CIBLANT PYGOBJECT */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Assure une prise en charge de l'objet Gtk.WIdget. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_gtk_widget_is_registered(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gtk_mod; /* Module Python Gtk */ + PyObject *widget_type; /* Module "GtkWidget" */ + + /** + * Afin d'éviter le message d'avertissement suivant, la version attendue + * est demandée : + * + * PyGIWarning: Gtk was imported without specifying a version first. + * Use gi.require_version('Gtk', '4.0') before import to ensure that the right version gets loaded. + * + */ + result = import_namespace_from_gi_repository("Gtk", "4.0"); + + if (result) + { + gtk_mod = PyImport_ImportModule("gi.repository.Gtk"); + assert(gtk_mod != NULL); + + widget_type = PyObject_GetAttrString(gtk_mod, "Widget"); + + result = (widget_type != NULL); + + Py_DECREF(gtk_mod); + Py_XDECREF(widget_type); + + } + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en instance de composant GTK. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_gtk_widget(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + PyObject *gtk_mod; /* Module Python Gtk */ + PyObject *widget_type; /* Module "GtkWidget" */ + int ret; /* Bilan d'une conversion */ + + result = 0; + + gtk_mod = PyImport_ImportModule("gi.repository.Gtk"); + + if (gtk_mod == NULL) + { + PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module"); + goto done; + } + + widget_type = PyObject_GetAttrString(gtk_mod, "Widget"); + + Py_DECREF(gtk_mod); + + ret = PyObject_TypeCheck(arg, (PyTypeObject *)widget_type); + + Py_DECREF(widget_type); + + if (!ret) + { + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GTK widget"); + goto done; + } + + *((GtkWidget **)dst) = GTK_WIDGET(pygobject_get(arg)); + + result = 1; + + done: + + return result; + +} diff --git a/src/plugins/dt.h b/plugins/pychrysalide/helpers-ui.h index 5c92593..b575905 100644 --- a/src/plugins/dt.h +++ b/plugins/pychrysalide/helpers-ui.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * dt.h - prototypes pour la possibilité de créer de nouveaux types de façon dynamique + * helpers-ui.h - prototypes pour la simplification des interactions UI de base avec Python * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,30 +21,24 @@ */ -#ifndef _PLUGINS_DT_H -#define _PLUGINS_DT_H +#ifndef _PLUGINS_PYCHRYSALIDE_HELPERS_UI_H +#define _PLUGINS_PYCHRYSALIDE_HELPERS_UI_H -#include <glib-object.h> +#include <Python.h> #include <stdbool.h> -/* ----------------------- ACCOMPAGNEMENTS DES NOUVEAUX TYPES ----------------------- */ +/* --------------------------- CONFORTS CIBLANT PYGOBJECT --------------------------- */ -/* Lance le support de dérivations de types dans Chrysalide. */ -bool init_chrysalide_dynamic_types(void); +/* Assure une prise en charge de l'objet Gtk.WIdget. */ +bool ensure_gtk_widget_is_registered(void); -/* Arrête le support de dérivations de types dans Chrysalide. */ -void exit_chrysalide_dynamic_types(void); +/* Tente de convertir en instance de composant GTK. */ +int convert_to_gtk_widget(PyObject *, void *); -/* Fournit un identifiant GLib pour un nouveau type. */ -GType build_dynamic_type(GType, const char *, GClassInitFunc, gconstpointer, GInstanceInitFunc); -/* Crée un objet à partir d'un type, dynamique ou classique. */ -gpointer create_object_from_type(GType); - - -#endif /* _PLUGINS_PYCHRYSALIDE_DT_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_HELPERS_UI_H */ diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c index 0ca133f..4ff768c 100644 --- a/plugins/pychrysalide/helpers.c +++ b/plugins/pychrysalide/helpers.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * helpers.c - simplification des interactions de base avec Python * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,14 +32,10 @@ #include <stdlib.h> #include <string.h> #include <strings.h> -#ifdef INCLUDE_GTK_SUPPORT -# include <gtk/gtk.h> -#endif #include <i18n.h> #include <common/extstr.h> -#include <plugins/dt.h> #include "access.h" @@ -217,6 +213,53 @@ bool has_python_method(PyObject *module, const char *method) * * * Paramètres : target = propriétaire de la routine visée. * * method = désignation de la fonction à appeler. * +* * +* Description : Indique si une routine Python possède une implémentation. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool has_python_implementation_method(PyObject *module, const char *method) +{ + bool result; /* Bilan à retourner */ + PyObject *func; /* Fonction visée */ + const PyMethodDef *def; /* Définition de la fonction */ + + result = (PyObject_HasAttrString(module, method) == 1); + + if (result) + { + func = PyObject_GetAttrString(module, method); + assert(func != NULL); + + result = PyCallable_Check(func); + + if (func->ob_type == &PyCFunction_Type) + { + def = ((PyCFunctionObject *)func)->m_ml; + + assert(strcmp(def->ml_name, method) == 0); + + result = (def->ml_meth != not_yet_implemented_method); + + } + + Py_DECREF(func); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : target = propriétaire de la routine visée. * +* method = désignation de la fonction à appeler. * * args = arguments à associer à l'opération. * * * * Description : Appelle une routine Python. * @@ -273,12 +316,6 @@ PyObject *run_python_method(PyObject *module, const char *method, PyObject *args PyErr_Restore(type, value, traceback); - if (result == NULL && PyErr_Occurred() != NULL) - PyErr_Print(); - - if (result == NULL) - Py_Exit(EXIT_FAILURE); - return result; } @@ -523,75 +560,10 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type) * * ******************************************************************************/ -PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type parent version Python */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = pygobject_lookup_class(gbase); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(gbase, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : type = type du nouvel objet à mettre en place. * -* gbase = type de base natif. * -* cinit = procédure d'initialisation de la classe associée. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition.* -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, GClassInitFunc cinit, PyObject *args, PyObject *kwds) +PyObject *python_abstract_constructor(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds) { PyObject *result; /* Objet à retourner */ PyTypeObject *base; /* Type parent version Python */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ /* Validations diverses */ @@ -604,24 +576,6 @@ PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GTy goto exit; } - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(gbase, type->tp_name, cinit, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ result = PyType_GenericNew(type, args, kwds); @@ -1165,6 +1119,60 @@ int forward_pygobjet_init(PyObject *self) /****************************************************************************** * * +* Paramètres : type = type Python à ausculter. * +* * +* Description : Détermine si un type Python est implémenté en C ou non. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool pytype_has_native_implementation(const PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + GType gtype; /* Type associé à la classe */ + + static GQuark pygobject_custom_key = 0; /* Clef d'accès direct */ + + /** + * Dans les sources de PyGObject, la fonction pyg_type_register() de + * gi/gimodule.c, appelée depuis gi/types.py, contient la bribe de code + * suivante : + * + * // Mark this GType as a custom python type + * g_type_set_qdata(instance_type, pygobject_custom_key, + * GINT_TO_POINTER (1)); + * + * La fonction pyi_object_register_types() de gi/pygobject-object.c indique + * la clef associée au Quark : + * + * pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + * + * Enfin, une fonction inspirante est codée dans le fichier gi/pygi-type.c : + * + * gboolean pyg_gtype_is_custom(GType gtype) + * { + * return g_type_get_qdata (gtype, pygobject_custom_key) != NULL; + * } + * + */ + + if (pygobject_custom_key == 0) + pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + + gtype = pyg_type_from_object((PyObject *)type); + + result = (g_type_get_qdata(gtype, pygobject_custom_key) == NULL); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : arg = argument quelconque à tenter de convertir. * * dst = destination des valeurs récupérées en cas de succès. * * * @@ -1249,122 +1257,6 @@ int convert_to_gobject(PyObject *arg, void *dst) #if 0 -#ifdef INCLUDE_GTK_SUPPORT - - -/****************************************************************************** -* * -* Paramètres : arg = argument quelconque à tenter de convertir. * -* dst = destination des valeurs récupérées en cas de succès. * -* * -* Description : Tente de convertir en instance de composant GTK. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_gtk_widget(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - PyObject *gtk_mod; /* Module Python Gtk */ - PyObject *widget_type; /* Module "GtkWidget" */ - int ret; /* Bilan d'une conversion */ - - result = 0; - - gtk_mod = PyImport_ImportModule("gi.repository.Gtk"); - - if (gtk_mod == NULL) - { - PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module"); - goto done; - } - - widget_type = PyObject_GetAttrString(gtk_mod, "Widget"); - - Py_DECREF(gtk_mod); - - ret = PyObject_TypeCheck(arg, (PyTypeObject *)widget_type); - - Py_DECREF(widget_type); - - if (!ret) - { - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GTK widget"); - goto done; - } - - *((GtkWidget **)dst) = GTK_WIDGET(pygobject_get(arg)); - - result = 1; - - done: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : arg = argument quelconque à tenter de convertir. * -* dst = destination des valeurs récupérées en cas de succès. * -* * -* Description : Tente de convertir en instance de conteneur GTK. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_gtk_container(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - PyObject *gtk_mod; /* Module Python Gtk */ - PyObject *container_type; /* Module "GtkContainer" */ - int ret; /* Bilan d'une conversion */ - - result = 0; - - gtk_mod = PyImport_ImportModule("gi.repository.Gtk"); - - if (gtk_mod == NULL) - { - PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module"); - goto done; - } - - container_type = PyObject_GetAttrString(gtk_mod, "Container"); - - Py_DECREF(gtk_mod); - - ret = PyObject_TypeCheck(arg, (PyTypeObject *)container_type); - - Py_DECREF(container_type); - - if (!ret) - { - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GTK container"); - goto done; - } - - *((GtkContainer **)dst) = GTK_CONTAINER(pygobject_get(arg)); - - result = 1; - - done: - - return result; - -} - - -#endif - - /****************************************************************************** * * * Paramètres : color = couleur dans sa définition native à copier. * @@ -1492,6 +1384,109 @@ int convert_to_gdk_rgba(PyObject *arg, void *dst) +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en tableau de chaînes de caractères. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_sequence_to_charp_array(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + charp_array_t *array; /* Tableau à constituer */ + size_t i; /* Boucle de parcours */ + PyObject *value; /* Valeur brute d'un élément */ + + array = (charp_array_t *)dst; + + /* Nettoyage ? */ + if (arg == NULL) + { + result = 1; + goto clean; + } + + else + { + result = 0; + + if (PySequence_Check(arg) != 1) + goto done; + + array->length = PySequence_Length(arg); + + array->values = calloc(array->length, sizeof(char *)); + + for (i = 0; i < array->length; i++) + { + value = PySequence_ITEM(arg, i); + + if (!PyUnicode_Check(value)) + { + Py_DECREF(value); + goto clean; + } + + array->values[i] = strdup(PyUnicode_DATA(value)); + + Py_DECREF(value); + + } + + result = Py_CLEANUP_SUPPORTED; + + } + + done: + + return result; + + clean: + + clean_charp_array(array); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : array = tableau de chaînes de caractères à traiter. * +* * +* Description : Libère de la mémoire un tableau de chaînes de caractères. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void clean_charp_array(charp_array_t *array) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < array->length; i++) + if (array->values[i] != NULL) + free(array->values[i]); + + if (array->values != NULL) + free(array->values); + + array->values = NULL; + array->length = 0; + +} + + + /* ---------------------------------------------------------------------------------- */ /* TRANSFERT DES VALEURS CONSTANTES */ /* ---------------------------------------------------------------------------------- */ diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h index cc82bba..f1c6337 100644 --- a/plugins/pychrysalide/helpers.h +++ b/plugins/pychrysalide/helpers.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * helpers.h - prototypes pour la simplification des interactions de base avec Python * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -34,6 +34,9 @@ #endif +#include <i18n.h> + + /* ---------------------- ACCELERATEURS POUR PYTHON UNIQUEMENT ---------------------- */ @@ -47,6 +50,9 @@ int convert_to_callable(PyObject *, void *); /* Indique si une routine Python existe ou non. */ bool has_python_method(PyObject *, const char *); +/* Indique si une routine Python possède une implémentation. */ +bool has_python_implementation_method(PyObject *, const char *); + /* Appelle une routine Python. */ PyObject *run_python_method(PyObject *, const char *, PyObject *); @@ -126,7 +132,7 @@ bool register_python_module_object(PyObject *, PyTypeObject *); PYTHON_GETSET_DEF("is_" #name, base ## _is_ ## name, NULL, ATTRIB_RO doc, NULL) #define PYTHON_HAS_DEF_FULL(name, base, doc) \ - PYTHON_GETSET_DEF(#name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL) + PYTHON_GETSET_DEF("has_" #name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL) #define PYTHON_RAWGET_DEF_FULL(name, base, doc) \ PYTHON_GETSET_DEF(#name, base ## _ ## name, NULL, ATTRIB_RO doc, NULL) @@ -152,14 +158,11 @@ bool register_python_module_object(PyObject *, PyTypeObject *); * * Cf. http://stackoverflow.com/questions/20432335/can-python-abstract-base-classes-inherit-from-c-extensions */ -#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new +#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new // REMME /* Accompagne la création d'une instance dérivée en Python. */ -PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *, GType, PyObject *, PyObject *); - -/* Accompagne la création d'une instance dérivée en Python. */ -PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *, GType, GClassInitFunc, PyObject *, PyObject *); +PyObject *python_abstract_constructor(PyTypeObject *, GType, PyObject *, PyObject *); #define CREATE_DYN_CONSTRUCTOR(pyname, gbase) \ @@ -167,22 +170,41 @@ static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *); static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \ { \ PyObject *result; /* Objet à retourner */ \ - result = python_constructor_with_dynamic_gtype(type, gbase, args, kwds); \ + result = PyType_GenericNew(type, args, kwds); \ return result; \ } -#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase, cinit) \ +#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase) \ static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *); \ static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \ { \ PyObject *result; /* Objet à retourner */ \ - result = python_abstract_constructor_with_dynamic_gtype(type, gbase, (GClassInitFunc)cinit, \ - args, kwds); \ + result = python_abstract_constructor(type, gbase, args, kwds); \ return result; \ } +/** + * Les initialisations de classes engagées par les appels à pyg_register_class_init() + * ne se déclenchent qu'après les initialisations complètes des classes côté GObject. + * + * Typiquement, pour une déclinaison Python du type PythonModule, sont appelées + * successivement les fonctions suivantes : + * - g_plugin_module_class_init() ; + * - g_python_plugin_class_init() ; + * - py_plugin_module_init_gclass(). + * + * Il est alors impératif de considérer les pointeurs de fonction déjà en place + * afin de ne par remplacer les implémentations de GPythonPlugin par les + * wrappers par défaut de PythonModule. + */ + +#define PY_CLASS_SET_WRAPPER(field, ptr) \ + if (field == NULL) \ + field = ptr; + + /* Marque l'interdiction d'une instanciation depuis Python. */ PyObject *no_python_constructor_allowed(PyTypeObject *, PyObject *, PyObject *); @@ -206,6 +228,41 @@ PyTypeObject *define_python_dynamic_type(const PyTypeObject *); /** + * Prise en compte d'éventuelles exceptions levées dans les implémentations. + * + * Par exemple : + * - du code Python exécute une fonction implémentée en C ; + * - cette dernière fait appel à un Wrapper C qui sollicite du code + * d'implémentation Python. + * + * Cette seconde étape peut lever une exception (impletation manquante ou + * implémentation levant une exception. + * + * Les codes C des étapes 1 et 2 ne dispose pas de mécanismes pour transmettre + * le détail des éventuelles exceptions, mais Python le mémorise. + */ + +#define UPDATE_RESULT_IF_RAISED_EXCEPTION(val) \ + do \ + { \ + if (PyErr_Occurred() != NULL) \ + result = val; \ + } \ + while (0) + +#define CLEAN_RESULT_IF_RAISED_EXCEPTION(val) \ + do \ + { \ + if (PyErr_Occurred() != NULL) \ + { \ + Py_XDECREF(result); \ + result = NULL; \ + } \ + } \ + while (0) + + +/** * pygobject_new() prend en compte les références flottantes au moment de la * construction d'un objet Python. * @@ -231,11 +288,23 @@ bool register_class_for_pygobject(PyObject *, GType, PyTypeObject *); bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *, const GInterfaceInfo *); /* Enregistre un type Python dérivant d'un type GLib dynamique. */ -bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); +bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); // REMME /* Fait suivre à la partie GObject une initialisation nouvelle. */ int forward_pygobjet_init(PyObject *); +/* Détermine si un type Python est implémenté en C ou non. */ +bool pytype_has_native_implementation(const PyTypeObject *); + +#define check_for_native_parent(obj) \ + ({ \ + bool __result; \ + __result = pytype_has_native_implementation((obj)->ob_type->tp_base); \ + if (!__result) \ + PyErr_SetString(PyExc_RuntimeError, _("object parent is not a native type")); \ + __result; \ + }) + /* Tente de convertir en valeur GType. */ int convert_to_gtype(PyObject *, void *); @@ -244,17 +313,6 @@ int convert_to_gobject(PyObject *, void *); #if 0 -#ifdef INCLUDE_GTK_SUPPORT - -/* Tente de convertir en instance de composant GTK. */ -int convert_to_gtk_widget(PyObject *, void *); - -/* Tente de convertir en instance de conteneur GTK. */ -int convert_to_gtk_container(PyObject *, void *); - -#endif - - #if !defined(INCLUDE_GTK_SUPPORT) && !defined(HOMEMADE_RGBA) # define HOMEMADE_RGBA @@ -283,6 +341,21 @@ int convert_to_gdk_rgba(PyObject *, void *); #endif +/* Tableau de chaînes de caractères converti */ +typedef struct _charp_array_t +{ + char **values; /* Liste de chaînes textuelles */ + size_t length; /* Taille de cette liste */ + +} charp_array_t; + +/* Tente de convertir en tableau de chaînes de caractères. */ +int convert_to_sequence_to_charp_array(PyObject *, void *); + +/* Libère de la mémoire un tableau de chaînes de caractères. */ +void clean_charp_array(charp_array_t *); + + /* ----------------------- TRANSFERT DES VALEURS CONSTANTES ------------------------- */ diff --git a/plugins/pychrysalide/plugins/Makefile.am b/plugins/pychrysalide/plugins/Makefile.am index bb9ed5d..abadb7f 100644 --- a/plugins/pychrysalide/plugins/Makefile.am +++ b/plugins/pychrysalide/plugins/Makefile.am @@ -2,10 +2,10 @@ noinst_LTLIBRARIES = libpychrysaplugins.la libpychrysaplugins_la_SOURCES = \ - constants.h constants.c \ - plugin.h plugin.c \ module.h module.c \ - translate.h translate.c + plugin.h plugin.c \ + python-int.h \ + python.h python.c libpychrysaplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/plugins/constants.c b/plugins/pychrysalide/plugins/constants.c deleted file mode 100644 index 7e20e4c..0000000 --- a/plugins/pychrysalide/plugins/constants.c +++ /dev/null @@ -1,147 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * constants.c - ajout des constantes principales - * - * Copyright (C) 2020 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "constants.h" - - -#include <plugins/plugin-def.h> - - -#include "../helpers.h" - - - -/****************************************************************************** -* * -* Paramètres : type = type dont le dictionnaire est à compléter. * -* * -* Description : Définit les constantes relatives aux greffons Python. * -* * -* Retour : true en cas de succès de l'opération, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool define_plugin_module_constants(PyTypeObject *type) -{ - bool result; /* Bilan à retourner */ - PyObject *values; /* Groupe de valeurs à établir */ - - result = true; - - values = PyDict_New(); - - if (result) result = add_const_to_group(values, "BASIC_NONE", PGA_BASIC_NONE); - if (result) result = add_const_to_group(values, "PLUGIN_INIT", PGA_PLUGIN_INIT); - if (result) result = add_const_to_group(values, "PLUGIN_LOADED", PGA_PLUGIN_LOADED); - if (result) result = add_const_to_group(values, "PLUGIN_EXIT", PGA_PLUGIN_EXIT); - if (result) result = add_const_to_group(values, "NATIVE_PLUGINS_LOADED", PGA_NATIVE_PLUGINS_LOADED); - if (result) result = add_const_to_group(values, "ALL_PLUGINS_LOADED", PGA_ALL_PLUGINS_LOADED); - if (result) result = add_const_to_group(values, "TYPE_BUILDING", PGA_TYPE_BUILDING); - if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME); - if (result) result = add_const_to_group(values, "PANEL_CREATION", PGA_PANEL_CREATION); - if (result) result = add_const_to_group(values, "PANEL_DOCKING", PGA_PANEL_DOCKING); - if (result) result = add_const_to_group(values, "CONTENT_EXPLORER", PGA_CONTENT_EXPLORER); - if (result) result = add_const_to_group(values, "CONTENT_RESOLVER", PGA_CONTENT_RESOLVER); - if (result) result = add_const_to_group(values, "CONTENT_ANALYZED", PGA_CONTENT_ANALYZED); - if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_STARTED", PGA_FORMAT_ANALYSIS_STARTED); - if (result) result = add_const_to_group(values, "FORMAT_PRELOAD", PGA_FORMAT_PRELOAD); - if (result) result = add_const_to_group(values, "FORMAT_ATTACH_DEBUG", PGA_FORMAT_ATTACH_DEBUG); - if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_ENDED", PGA_FORMAT_ANALYSIS_ENDED); - if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_STARTED", PGA_FORMAT_POST_ANALYSIS_STARTED); - if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_ENDED", PGA_FORMAT_POST_ANALYSIS_ENDED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_STARTED", PGA_DISASSEMBLY_STARTED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_RAW", PGA_DISASSEMBLY_RAW); - if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_LINK", PGA_DISASSEMBLY_HOOKED_LINK); - if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_POST", PGA_DISASSEMBLY_HOOKED_POST); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LIMITED", PGA_DISASSEMBLY_LIMITED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LOOPS", PGA_DISASSEMBLY_LOOPS); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LINKED", PGA_DISASSEMBLY_LINKED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_GROUPED", PGA_DISASSEMBLY_GROUPED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_RANKED", PGA_DISASSEMBLY_RANKED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_ENDED", PGA_DISASSEMBLY_ENDED); - if (result) result = add_const_to_group(values, "DETECTION_OBFUSCATORS", PGA_DETECTION_OBFUSCATORS); - - if (!result) - { - Py_DECREF(values); - goto exit; - } - - result = attach_constants_group_to_type(type, true, "PluginAction", values, - "Features with which plugins can extend the core of Chrysalide."); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : arg = argument quelconque à tenter de convertir. * -* dst = destination des valeurs récupérées en cas de succès. * -* * -* Description : Tente de convertir en constante PluginAction. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_plugin_action(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - unsigned long value; /* Valeur récupérée */ - - result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); - - switch (result) - { - case -1: - /* L'exception est déjà fixée par Python */ - result = 0; - break; - - case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to PluginAction"); - break; - - case 1: - value = PyLong_AsUnsignedLong(arg); - *((PluginAction *)dst) = value; - break; - - default: - assert(false); - break; - - } - - return result; - -} diff --git a/plugins/pychrysalide/plugins/module.c b/plugins/pychrysalide/plugins/module.c index 1c7b326..ce94611 100644 --- a/plugins/pychrysalide/plugins/module.c +++ b/plugins/pychrysalide/plugins/module.c @@ -33,8 +33,8 @@ #include <plugins/pglist.h> -#include "constants.h" #include "plugin.h" +#include "python.h" #include "../helpers.h" @@ -46,7 +46,7 @@ static PyObject *py_plugins_get_plugin_by_name(PyObject *, PyObject *); static PyObject *py_plugins_get_all_plugins(PyObject *, PyObject *); /* Fournit les greffons offrant le service demandé. */ -static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *); +//static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *); @@ -162,7 +162,7 @@ static PyObject *py_plugins_get_all_plugins(PyObject *self, PyObject *args) } - +#if 0 /****************************************************************************** * * * Paramètres : self = NULL car méthode statique. * @@ -219,7 +219,7 @@ static PyObject *py_plugins_get_all_plugins_for_action(PyObject *self, PyObject return result; } - +#endif /****************************************************************************** * * @@ -248,7 +248,7 @@ bool add_plugins_module(PyObject *super) static PyMethodDef py_plugins_methods[] = { PY_PLUGINS_GET_PLUGIN_BY_NAME_METHOD, PY_PLUGINS_GET_ALL_PLUGINS_METHOD, - PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD, + //PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD, { NULL } }; @@ -293,6 +293,7 @@ bool populate_plugins_module(void) result = true; if (result) result = ensure_python_plugin_module_is_registered(); + if (result) result = ensure_python_python_plugin_is_registered(); assert(result); diff --git a/plugins/pychrysalide/plugins/plugin.c b/plugins/pychrysalide/plugins/plugin.c index de070cb..78f57ba 100644 --- a/plugins/pychrysalide/plugins/plugin.c +++ b/plugins/pychrysalide/plugins/plugin.c @@ -25,22 +25,17 @@ #include "plugin.h" -#include <assert.h> -#include <libgen.h> #include <malloc.h> #include <pygobject.h> #include <string.h> -#include <common/extstr.h> -#include <plugins/dt.h> +#include <common/compiler.h> #include <plugins/plugin-int.h> #include <plugins/pglist.h> #include <plugins/self.h> -#include "constants.h" -#include "translate.h" #include "../access.h" #include "../core.h" #include "../helpers.h" @@ -52,85 +47,53 @@ /* Initialise la classe des greffons d'extension. */ -static void py_plugin_module_init_gclass(GPluginModuleClass *, gpointer); +static int py_plugin_module_init_gclass(GPluginModuleClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE, py_plugin_module_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds); -/* Encadre une étape de la vie d'un greffon. */ -static bool py_plugin_module_manage_wrapper(GPluginModule *); - -/* Assiste la désactivation d'un greffon. */ -static bool py_plugin_module_exit(GPluginModule *); - -/* Accompagne la fin du chargement des modules natifs. */ -static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *, PluginAction); - -/* Fournit le nom brut associé au greffon par défaut. */ -static PyObject *py_plugin_module_get_modname_by_default(PyObject *, PyObject *); +/* Pointe le fichier contenant le greffon manipulé. */ +static char *py_plugin_module_get_filename_wrapper(const GPluginModule *); /* Fournit le nom brut associé au greffon. */ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *); -#if 0 - -#ifdef INCLUDE_GTK_SUPPORT - -/* Complète une liste de resources pour thème. */ -static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, gboolean, char ***, size_t *); - -/* Rend compte de la création d'un panneau. */ -static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *, PluginAction, GPanelItem *); - -/* Rend compte d'un affichage ou d'un retrait de panneau. */ -static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *, PluginAction, GPanelItem *, bool); - -#endif - -/* Procède à une opération liée à un contenu binaire. */ -static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); - -/* Procède à une opération liée à un contenu chargé. */ -static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *, PluginAction, GLoadedContent *, wgroup_id_t, GtkStatusStack *); +/* Prend acte de l'activation du greffon. */ +static bool py_plugin_module_enable_wrapper(GPluginModule *); -/* Procède à une opération liée à l'analyse d'un format. */ -static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *, PluginAction, GKnownFormat *, wgroup_id_t, GtkStatusStack *); +/* Prend acte de la désactivation du greffon. */ +static bool py_plugin_module_disable_wrapper(GPluginModule *); -/* Procède à un préchargement de format de fichier. */ -static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *); -/* Procède au rattachement d'éventuelles infos de débogage. */ -static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *, PluginAction, GExeFormat *); -/* Exécute une action pendant un désassemblage de binaire. */ -static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *); - -/* Effectue la détection d'effets d'outils externes. */ -static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *, PluginAction, const GLoadedContent *, bool, char ***, size_t *); - -#endif +/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ +/* Affiche un message dans le journal des messages système. */ +static PyObject *py_plugin_module_log_message(PyObject *, PyObject *); -/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ +/* Indique le nom associé à un greffon. */ +static PyObject *py_plugin_module_get_name(PyObject *, void *); +/* Fournit une description fonctionnelle d'un greffon. */ +static PyObject *py_plugin_module_get_desc(PyObject *, void *); -/* Construit le nom d'un fichier de configuration du greffon. */ -static PyObject *py_plugin_module_build_config_filename(PyObject *, PyObject *); +/* Fournit la version d'un greffon et de ses fonctionnalités. */ +static PyObject *py_plugin_module_get_version(PyObject *, void *); -/* Affiche un message dans le journal des messages système. */ -static PyObject *py_plugin_module_log_message(PyObject *, PyObject *); +/* Fournit l'URL des ressources en ligne liées à un greffon. */ +static PyObject *py_plugin_module_get_url(PyObject *, void *); -/* Fournit le nom brut associé au greffon. */ -static PyObject *py_plugin_module_get_modname(PyObject *, void *); +/* Fournit la liste des dépendances d'un greffon donné. */ +static PyObject *py_plugin_module_get_requirements(PyObject *, void *); /* Indique le fichier contenant le greffon manipulé. */ static PyObject *py_plugin_module_get_filename(PyObject *, void *); -/* Fournit la description du greffon dans son intégralité. */ -static PyObject *py_plugin_module_get_interface(PyObject *, void *); +/* Fournit le nom brut associé au greffon. */ +static PyObject *py_plugin_module_get_modname(PyObject *, void *); @@ -141,47 +104,26 @@ static PyObject *py_plugin_module_get_interface(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des greffons d'extension. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused) +static int py_plugin_module_init_gclass(GPluginModuleClass *gclass, PyTypeObject *pyclass) { - class->init = NULL; - class->manage = py_plugin_module_manage_wrapper; - class->exit = py_plugin_module_exit; + PY_CLASS_SET_WRAPPER(gclass->get_filename, py_plugin_module_get_filename_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_modname, py_plugin_module_get_modname_wrapper); - class->plugins_loaded = py_plugin_module_notify_plugins_loaded_wrapper; + PY_CLASS_SET_WRAPPER(gclass->enable, py_plugin_module_enable_wrapper); + PY_CLASS_SET_WRAPPER(gclass->disable, py_plugin_module_disable_wrapper); - class->get_modname = py_plugin_module_get_modname_wrapper; - -#if 0 - -#ifdef INCLUDE_GTK_SUPPORT - class->include_theme = py_plugin_module_include_theme_wrapper; - class->notify_panel = py_plugin_module_notify_panel_creation_wrapper; - class->notify_docking = py_plugin_module_notify_panel_docking_wrapper; -#endif - - class->handle_content = py_plugin_module_handle_binary_content_wrapper; - class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper; - - class->handle_fmt_analysis = py_plugin_module_handle_known_format_analysis_wrapper; - class->preload_format = py_plugin_module_preload_binary_format_wrapper; - class->attach_debug = py_plugin_module_attach_debug_format_wrapper; - - class->process_disass = py_plugin_module_process_disassembly_event_wrapper; - - class->detect = py_plugin_module_detect_external_tools_wrapper; - -#endif + return 0; } @@ -202,45 +144,56 @@ static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unu static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) { + const char *desc; /* Description plus loquace */ + const char *version; /* Version du greffon */ + const char *url; /* Site Web associé */ + const char *name; /* Désignation humaine courte */ + charp_array_t required; /* Liste des dépendances */ int ret; /* Bilan d'un appel */ GPluginModule *plugin; /* Greffon à manipuler */ - plugin_interface *iface; /* Interface à constituer */ - GPluginModule *dependency; /* Module nécessaire */ - PyObject *value; /* Valeur à présence imposée */ - size_t i; /* Boucle de parcours */ - PyObject *action; /* Identifiant d'une action */ + + static char *kwlist[] = { "name", "desc", "version", "url", "required", NULL }; #define PLUGIN_MODULE_DOC \ - "PythonModule is the class allowing the creation of Chrysalide plugins" \ - " for Python." \ + "PluginModule is the core class handling the Chrysalide plugins." \ + " Python plugins should inherit the pychrysalide.plugins.PythonPlugin" \ + " instead of this one." \ "\n" \ "Calls to the *__init__* constructor of this abstract object expect" \ - " no particular argument.\n" \ + " the following arguments as keyword parameters:\n" \ + "* *name*: a string providing a key name for the plugin;\n" \ + "* *desc* (optional): a string for a human readable description of the" \ + " features provided by the plugin;\n" \ + "* *version* (optional): a string providing the version of the plugin;" \ + " Version format is free;\n" \ + "* *url* (optional): a string for the homepage describing the plugin;\n"\ + "* *required* (optional): dependencies of the plugin." \ "\n" \ - "Several items have to be defined as class attributes in the final" \ - " class:\n" \ - "* *_name*: a string providing a small name for the plugin;\n" \ - "* *_desc*: a string for a human readable description of the plugin;\n" \ - "* *_version*: a string providing the version of the plugin;\n" \ - "* *_url*: a string for the homepage describing the plugin;\n" \ - "* *_actions*: a tuple of" \ - " pychrysalide.plugins.PluginModule.PluginAction defining the features" \ - " the plugin is bringing; this list can be empty.\n" \ + "Dependency to the *PyChrysalide* plugin is added automatically if not" \ + " specified.\n" \ "\n" \ - "Depending on the implemented actions, some of the following methods" \ - " have to be defined for new classes:\n" \ - "* pychrysalide.plugins.PluginModule._init_config();\n" \ - "* pychrysalide.plugins.PluginModule._notify_plugins_loaded();\n" \ - "* pychrysalide.plugins.PluginModule._include_theme();\n" \ - "* pychrysalide.plugins.PluginModule._on_panel_creation;\n" \ - "* pychrysalide.plugins.PluginModule._on_panel_docking();\n" \ - "* pychrysalide.plugins.PluginModule._handle_binary_content();\n" \ - "* pychrysalide.plugins.PluginModule._handle_loaded_content();\n" \ - "* pychrysalide.plugins.PluginModule._handle_format_analysis();\n" \ - "* pychrysalide.plugins.PluginModule._preload_format();\n" \ - "* pychrysalide.plugins.PluginModule._attach_debug_format();\n" \ - "* pychrysalide.plugins.PluginModule._process_disassembly_event();\n" \ - "* pychrysalide.plugins.PluginModule._detect_external_tools()." + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.plugins.PluginModule._get_filename();\n" \ + "* pychrysalide.plugins.PluginModule._get_modname();\n" \ + "* pychrysalide.plugins.PluginModule._enable();\n" \ + "* pychrysalide.plugins.PluginModule._disable()." + + /* Récupération des paramètres */ + + desc = NULL; + version = NULL; + url = NULL; + required.values = NULL; + required.length = 0; + + ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|sssO&", kwlist, + &name, &desc, &version, &url, + convert_to_sequence_to_charp_array, &required); + if (!ret) return -1; + + required.values = realloc(required.values, ++required.length * sizeof(char *)); + + required.values[required.length - 1] = strdup("PyChrysalide"); /* Initialisation d'un objet GLib */ @@ -251,105 +204,17 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - iface = calloc(1, sizeof(plugin_interface)); - plugin->interface = iface; - -#define LOAD_PYTHON_IFACE(attr) \ - do \ - { \ - value = PyObject_GetAttrString(self, "_" #attr); \ - if (value == NULL) \ - { \ - PyErr_SetString(PyExc_TypeError, _("A '_" #attr "' class attributes is missing.")); \ - return -1; \ - } \ - if (PyUnicode_Check(value)) \ - { \ - iface->attr = strdup(PyUnicode_AsUTF8(value)); \ - Py_DECREF(value); \ - } \ - else \ - { \ - Py_DECREF(value); \ - PyErr_SetString(PyExc_TypeError, _("The '_" #attr "' class attributes must be a string.")); \ - return -1; \ - } \ - assert(iface->attr != NULL); \ - } \ - while (0); - - LOAD_PYTHON_IFACE(name); - LOAD_PYTHON_IFACE(desc); - LOAD_PYTHON_IFACE(version); - LOAD_PYTHON_IFACE(url); - - iface->container = false; - - /** - * Comme le greffon n'est pas passé par la résolution des dépendances, - * orchestrée par la fonction g_plugin_module_resolve_dependencies(), - * on simule l'effet attendu en obtenant une référence par un appel à - * get_plugin_by_name(). - * - * L'incrémentation des références doit coller au plus près de - * l'inscription nominative du greffon : en cas de sortie impromptue - * (lorsqu'une erreur intervient pendant un chargement par exemple), - * l'état de l'ensemble est ainsi cohérent au moment du retrait du - * greffon fautif via la fonction g_plugin_module_dispose(). - */ - - lock_plugin_list_for_reading(); - dependency = get_plugin_by_name("PyChrysalide", NULL); - unlock_plugin_list_for_reading(); - - assert(dependency != NULL); - - if (dependency == NULL) - { - PyErr_SetString(PyExc_TypeError, _("The internal name of the Python plugin has changed!")); - return -1; - } - - iface->required = malloc(sizeof(char *)); - iface->required[0] = "PyChrysalide"; - iface->required_count = 1; + STORE_PLUGIN_ABI(plugin); - /* Validation du reste de l'interface */ - - value = PyObject_GetAttrString(self, "_actions"); - - if (value == NULL) + if (!g_plugin_module_create(plugin, name, desc, version, url, + CONST_ARRAY_CAST(required.values, char), required.length)) { - PyErr_SetString(PyExc_TypeError, _("An '_actions' class attributes is missing.")); + clean_charp_array(&required); + PyErr_SetString(PyExc_ValueError, _("Unable to create plugin module.")); return -1; } - if (!PyTuple_Check(value)) - { - Py_DECREF(value); - PyErr_SetString(PyExc_TypeError, _("The '_actions' class attributes must be a tuple.")); - return -1; - } - - iface->actions_count = PyTuple_Size(value); - iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t)); - - for (i = 0; i < iface->actions_count; i++) - { - action = PyTuple_GetItem(value, i); - - if (!PyLong_Check(action)) - { - Py_DECREF(value); - PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action.")); - return -1; - } - - iface->actions[i] = PyLong_AsUnsignedLong(action); - - } - - Py_DECREF(value); + clean_charp_array(&required); return 0; @@ -358,56 +223,55 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * +* Paramètres : plugin = greffon à consulter. * * * -* Description : Encadre une étape de la vie d'un greffon. * +* Description : Pointe le fichier contenant le greffon manipulé. * * * -* Retour : Bilan de l'opération. * +* Retour : Chemin d'accès au greffon. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_plugin_module_manage_wrapper(GPluginModule *plugin) +static char *py_plugin_module_get_filename_wrapper(const GPluginModule *plugin) { - bool result; /* Bilan à faire remonter */ + char *result; /* Désignation brute à renvoyer*/ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_MANAGE_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _manage, "$self, action, /", \ - METH_VARARGS, \ - "Abstract method called to react to several steps of the plugin" \ - " life.\n" \ - "\n" \ - "The expected action is a" \ - " pychrysalide.plugins.PluginModule.PluginAction value.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *PLUGIN_LOADED*." \ +#define PLUGIN_MODULE_GET_FILENAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_filename, "$self, /", \ + METH_NOARGS, \ + "Abstract method providing the filename of the script.\n" \ + "\n" \ + "The result should be the string value pointing to the plugin" \ + " file path.\n" \ ) - result = true; + result = NULL; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_manage")) + if (has_python_method(pyobj, "_get_filename")) { - args = PyTuple_New(1); + pyret = run_python_method(pyobj, "_get_filename", NULL); - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(PGA_PLUGIN_LOADED)); + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("The returned raw name must be a string")); - pyret = run_python_method(pyobj, "_manage", args); + else + result = strdup(PyUnicode_DATA(pyret)); - result = (pyret == Py_True); + } Py_XDECREF(pyret); - Py_DECREF(args); } @@ -422,44 +286,62 @@ static bool py_plugin_module_manage_wrapper(GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * +* Paramètres : plugin = greffon à valider. * * * -* Description : Assiste la désactivation d'un greffon. * +* Description : Fournit le nom brut associé au greffon. * * * -* Retour : Bilan de l'opération. * +* Retour : Désignation brute du greffon. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_plugin_module_exit(GPluginModule *plugin) +static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) { - bool result; /* Bilan à faire remonter */ - plugin_interface *final; /* Interface finale conservée */ + char *result; /* Désignation brute à renvoyer*/ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan d'exécution */ - result = true; +#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_modname, "$self, /", \ + METH_NOARGS, \ + "Abstract method providing the raw module name of the loaded" \ + " plugin.\n" \ + "\n" \ + "The result should be a short string value.\n" \ +) - final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; + result = NULL; + + gstate = PyGILState_Ensure(); - if (final != NULL) + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_get_modname")) { - if (final->name != NULL) free(final->name); - if (final->desc != NULL) free(final->desc); - if (final->version != NULL) free(final->version); - if (final->url != NULL) free(final->url); + pyret = run_python_method(pyobj, "_get_modname", NULL); - assert(final->required_count == 1); + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("The returned raw name must be a string")); - if (final->required != NULL) - free(final->required); + else + result = strdup(PyUnicode_DATA(pyret)); - if (final->actions != NULL) - free(final->actions); + } - free(final); + Py_XDECREF(pyret); } + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + return result; } @@ -468,51 +350,45 @@ static bool py_plugin_module_exit(GPluginModule *plugin) /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * * * -* Description : Accompagne la fin du chargement des modules natifs. * +* Description : Prend acte de l'activation du greffon. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin, PluginAction action) +static bool py_plugin_module_enable_wrapper(GPluginModule *plugin) { + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _notify_plugins_loaded, "$self, action, /", \ - METH_VARARGS, \ - "Abstract method called once all the (native?) plugins are" \ - " loaded.\n" \ - "\n" \ - "The expected action is a" \ - " pychrysalide.plugins.PluginModule.PluginAction value.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *NATIVE_PLUGINS_LOADED* or *PLUGINS_LOADED*." \ +#define PLUGIN_MODULE_ENABLE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _enable, "$self, /", \ + METH_VARARGS, \ + "Abstract method called when the plugin gets enabled.\n" \ + "\n" \ + "The result is a boolean status: *False* if an implementation" \ + " failed, *True* otherwise." \ ) + result = true; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_notify_plugins_loaded")) + if (has_python_implementation_method(pyobj, "_enable")) { - args = PyTuple_New(1); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + pyret = run_python_method(pyobj, "_enable", NULL); - pyret = run_python_method(pyobj, "_notify_plugins_loaded", args); + result = (pyret == Py_True); Py_XDECREF(pyret); - Py_DECREF(args); } @@ -520,36 +396,6 @@ static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin PyGILState_Release(gstate); -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Fournit le nom brut associé au greffon par défaut. * -* * -* Retour : Désignation brute du greffon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GPluginModule *plugin; /* Version native du greffon */ - char *path; /* Chemin à traiter */ - - plugin = G_PLUGIN_MODULE(pygobject_get(self)); - - path = strdup(g_plugin_module_get_filename(plugin)); - - result = PyUnicode_FromString(basename(path)); - - free(path); - return result; } @@ -557,52 +403,44 @@ static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObjec /****************************************************************************** * * -* Paramètres : plugin = greffon à valider. * +* Paramètres : plugin = greffon à manipuler. * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Prend acte de la désactivation du greffon. * * * -* Retour : Désignation brute du greffon. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) +static bool py_plugin_module_disable_wrapper(GPluginModule *plugin) { - char *result; /* Désignation brute à renvoyer*/ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF_WITH \ -( \ - _get_modname, "$self, /", \ - METH_VARARGS, py_plugin_module_get_modname_by_default, \ - "(Abstract) method providing the raw module name of the plugin.\n" \ - " loaded.\n" \ - "\n" \ - "The result should be a short string value.\n" \ - "\n" \ - "A default implementation builds the module name from the Python" \ - " script filename." \ +#define PLUGIN_MODULE_DISABLE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _disable, "$self, /", \ + METH_VARARGS, \ + "Abstract method called when the plugin gets disabled.\n" \ + "\n" \ + "The result is a boolean status: *False* if an implementation" \ + " failed, *True* otherwise." \ ) - result = NULL; + result = true; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_get_modname")) + if (has_python_implementation_method(pyobj, "_disable")) { - pyret = run_python_method(pyobj, "_get_modname", NULL); + pyret = run_python_method(pyobj, "_disable", NULL); - if (!PyUnicode_Check(pyret)) - g_plugin_module_log_variadic_message(plugin, LMT_ERROR, - _("The returned raw name must be a string")); - - else - result = strdup(PyUnicode_DATA(pyret)); + result = (pyret == Py_True); Py_XDECREF(pyret); @@ -617,6 +455,15 @@ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) } + + + + + + + + + #if 0 #ifdef INCLUDE_GTK_SUPPORT @@ -1382,51 +1229,42 @@ static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule * * Paramètres : self = objet Python concerné par l'appel. * * args = arguments fournis à l'appel. * * * -* Description : Construit le nom d'un fichier de configuration du greffon. * +* Description : Affiche un message dans le journal des messages système. * * * -* Retour : Chemin d'accès déterminé, ou NULL en cas d'erreur. * +* Retour : Rien en équivalent Python. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject *args) +static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ - const char *final; /* Suffixe de fichier imposé */ - int create; /* Volonté de création */ - char *filename; /* Nom de fichier déterminé */ + LogMessageType type; /* Espèce du message */ + const char *msg; /* Contenu du message */ -#define PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD PYTHON_METHOD_DEF \ +#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \ ( \ - build_config_filename, "final, /, create=False", \ + log_message, "type, msg, /", \ METH_VARARGS, py_plugin_module, \ - "Build a filename suitable for the plugin configuration, ending with" \ - " the *final* suffix.\n" \ + "Display a message in the log window, in graphical mode, or in the" \ + " console output if none.\n" \ "\n" \ - "If the *create* parameter is set, the path to this filename is" \ - " created.\n" \ + "The type of the message has to be a pychrysalide.core.LogMessageType" \ + " value." \ "\n" \ - "The result is a string or None on failure." \ + "The only difference with the main pychrysalide.core.log_message()" \ + " function is that messages are automatically prefixed with the plugin" \ + " name here." \ ) - create = 0; - - if (!PyArg_ParseTuple(args, "s|p", &final, &create)) + if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg)) return NULL; - filename = g_plugin_module_build_config_filename(G_PLUGIN_MODULE(pygobject_get(self)), final, create); + g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); - if (filename != NULL) - { - result = PyUnicode_FromString(filename); - free(filename); - } - else - { - result = Py_None; - Py_INCREF(result); - } + result = Py_None; + Py_INCREF(result); return result; @@ -1435,45 +1273,35 @@ static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject /****************************************************************************** * * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Affiche un message dans le journal des messages système. * +* Description : Indique le nom associé à un greffon. * * * -* Retour : Rien en équivalent Python. * +* Retour : Désignation interne de l'extension, pour référence(s). * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) +static PyObject *py_plugin_module_get_name(PyObject *self, void *closure) { - PyObject *result; /* Bilan à retourner */ - LogMessageType type; /* Espèce du message */ - const char *msg; /* Contenu du message */ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + const char *name; /* Nom attribué au greffon */ -#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \ -( \ - log_message, "type, msg, /", \ - METH_VARARGS, py_plugin_module, \ - "Display a message in the log window, in graphical mode, or in the" \ - " console output if none.\n" \ - "\n" \ - "The type of the message has to be a pychrysalide.core.LogMessageType" \ - " value." \ - "\n" \ - "The only difference with the main pychrysalide.core.log_message()" \ - " function is that messages are automatically prefixed with the plugin" \ - " name here." \ +#define PLUGIN_MODULE_NAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + name, py_plugin_module, \ + "Name of the plugin. This string value is used to" \ + " reference the plugin as dependency." \ ) - if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg)) - return NULL; + plugin = G_PLUGIN_MODULE(pygobject_get(self)); - g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); + name = g_plugin_module_get_name(plugin); - result = Py_None; - Py_INCREF(result); + result = PyUnicode_FromString(name); return result; @@ -1485,32 +1313,31 @@ static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Fournit une description fonctionnelle d'un greffon. * * * -* Retour : Désignation brute du greffon. * +* Retour : Description textuelle associée à une extension ou NULL. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_desc(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - char *modname; /* Désignation brute */ + const char *desc; /* Description du greffon */ -#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - modname, py_plugin_module, \ - "Raw module name of the plugin." \ +#define PLUGIN_MODULE_DESC_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + desc, py_plugin_module, \ + "Optional plugin description as string or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - modname = g_plugin_module_get_modname(plugin); - result = PyUnicode_FromString(modname); + desc = g_plugin_module_get_desc(plugin); - free(modname); + result = PyUnicode_FromString(desc); return result; @@ -1522,30 +1349,32 @@ static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Indique le fichier contenant le greffon manipulé. * +* Description : Fournit la version d'un greffon et de ses fonctionnalités. * * * -* Retour : Chemin d'accès au greffon. * +* Retour : Version sous forme de chaîne de caractères ou None. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_version(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - const char *filename; /* Chemin d'accès associé */ + const char *version; /* Version du greffon */ -#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_VERSION_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - filename, py_plugin_module, \ - "Filename of the plugin." \ + version, py_plugin_module, \ + "Optional plugin version, in free format, as string," \ + " or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - filename = g_plugin_module_get_filename(plugin); - result = PyUnicode_FromString(filename); + version = g_plugin_module_get_version(plugin); + + result = PyUnicode_FromString(version); return result; @@ -1557,113 +1386,204 @@ static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit la description du greffon dans son intégralité. * +* Description : Fournit l'URL des ressources en ligne liées à un greffon. * * * -* Retour : Interfaçage renseigné. * +* Retour : URL de renvoi associée à une extension ou None. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_interface(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_url(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - const plugin_interface *iface; /* Interface liée à traduire */ + const char *url; /* URL associée au greffon */ -#define PLUGIN_MODULE_INTERFACE_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_URL_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - interface, py_plugin_module, \ - "Interface exported by the plugin..\n" \ - "\n" \ - "This property is a pychrysalide.StructObject instance." \ - "\n" \ - "The provided information is composed of the following" \ - " properties :\n" \ - "\n" \ - "* gtp_name;\n" \ - "* name;\n" \ - "* desc;\n" \ - "* version;\n" \ - "* url;\n" \ - "* container;\n" \ - "* required;\n" \ - "* actions.\n" \ - "\n" \ - "The *gtp_name* value may be *None* for non-native plugin." \ - " All other fields carry a string value except:\n" \ - "* *container*: a boolean status indicating if the plugin" \ - " can embed other plugins;\n" \ - "* *required*: a tuple of depedencies names;\n" \ - "* *actions*: a tuple of available features from the plugin"\ - " coded as pychrysalide.plugins.PluginModule.PluginAction" \ - " values." \ + url, py_plugin_module, \ + "Optional URL pointing to the plugin homepage as string" \ + " or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - iface = g_plugin_module_get_interface(plugin); - result = translate_plugin_interface_to_python(iface); + url = g_plugin_module_get_url(plugin); + + result = PyUnicode_FromString(url); return result; } -#if 0 + /****************************************************************************** * * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit la configuration mise en place pour le greffon. * +* Description : Fournit la liste des dépendances d'un greffon donné. * * * -* Retour : Configuration dédiée à l'extension. * +* Retour : Liste des noms d'extensions requises pour une extension. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_config(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_requirements(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - GGenConfig *config; /* Configuration associée */ + const char * const *required; /* Liste de dépendances */ + size_t count; /* Nombre de ces dépendances */ + size_t i; /* Boucle de parcours */ -#define PLUGIN_MODULE_CONFIG_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_REQUIREMENTS_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - config, py_plugin_module, \ - "Dedicated configuration for the plugin." \ - "\n" \ - "The value is a pychrysalide.glibext.GenConfig instance" \ - " or None if the configuration is not yet created.\n" \ - "\n" \ - "As configuration storage path depends on the plugin name," \ - " all plugin properties have to get fully loaded by the" \ - " core before the configuration can be setup." \ - "automatically" \ + requirements, py_plugin_module, \ + "Tuple of the plugin dependencies." \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + + required = g_plugin_module_get_requirements(plugin, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + PyTuple_SetItem(result, i, PyUnicode_FromString(required[i])); + + return result; + +} + + + + + + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + char *filename; /* Chemin d'accès associé */ + +#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + filename, py_plugin_module, \ + "Filename of the plugin." \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + + filename = g_plugin_module_get_filename(plugin); + + if (filename != NULL) + { + result = PyUnicode_FromString(filename); + + free(filename); + + } + + else + { + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as filename")); + + result = NULL; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + char *modname; /* Désignation brute */ + +#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + modname, py_plugin_module, \ + "Raw module name of the plugin." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - config = g_plugin_module_get_config(plugin); - if (config == NULL) + modname = g_plugin_module_get_modname(plugin); + + if (modname != NULL) { - result = Py_None; - Py_INCREF(result); + result = PyUnicode_FromString(modname); + + free(modname); + } else { - result = pygobject_new(G_OBJECT(config)); + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as modname")); - g_object_unref(G_OBJECT(config)); + result = NULL; } return result; } -#endif /****************************************************************************** @@ -1681,33 +1601,22 @@ static PyObject *py_plugin_module_get_config(PyObject *self, void *closure) PyTypeObject *get_python_plugin_module_type(void) { static PyMethodDef py_plugin_module_methods[] = { - PLUGIN_MODULE_MANAGE_WRAPPER, - PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER, + PLUGIN_MODULE_GET_FILENAME_WRAPPER, PLUGIN_MODULE_GET_MODNAME_WRAPPER, -#if 0 -#ifdef INCLUDE_GTK_SUPPORT - PLUGIN_MODULE_INCLUDE_THEME_WRAPPER, - PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER, - PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER, -#endif - PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER, - PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER, - PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER, - PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER, - PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER, - PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER, - PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER, - PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD, -#endif + PLUGIN_MODULE_ENABLE_WRAPPER, + PLUGIN_MODULE_DISABLE_WRAPPER, PLUGIN_MODULE_LOG_MESSAGE_METHOD, { NULL } }; static PyGetSetDef py_plugin_module_getseters[] = { - PLUGIN_MODULE_MODNAME_ATTRIB, + PLUGIN_MODULE_NAME_ATTRIB, + PLUGIN_MODULE_DESC_ATTRIB, + PLUGIN_MODULE_VERSION_ATTRIB, + PLUGIN_MODULE_URL_ATTRIB, + PLUGIN_MODULE_REQUIREMENTS_ATTRIB, PLUGIN_MODULE_FILENAME_ATTRIB, - PLUGIN_MODULE_INTERFACE_ATTRIB, - //PLUGIN_MODULE_CONFIG_ATTRIB, + PLUGIN_MODULE_MODNAME_ATTRIB, { NULL } }; @@ -1761,10 +1670,9 @@ bool ensure_python_plugin_module_is_registered(void) dict = PyModule_GetDict(module); - if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type)) - return false; + pyg_register_class_init(G_TYPE_PLUGIN_MODULE, (PyGClassInitFunc)py_plugin_module_init_gclass); - if (!define_plugin_module_constants(type)) + if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type)) return false; } @@ -1772,80 +1680,3 @@ bool ensure_python_plugin_module_is_registered(void) return true; } - - -/****************************************************************************** -* * -* Paramètres : modname = nom du module à charger. * -* filename = chemin d'accès au code Python à charger. * -* * -* Description : Crée un greffon à partir de code Python. * -* * -* Retour : Adresse de la structure mise en place ou NULL si erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPluginModule *create_python_plugin(const char *modname, const char *filename) -{ - GPluginModule *result; /* Structure à retourner */ - PyObject *name; /* Chemin d'accès pour Python */ - PyObject *module; /* Script Python chargé */ - PyObject *dict; /* Dictionnaire associé */ - PyObject *class; /* Classe à instancier */ - PyObject *instance; /* Instance Python du greffon */ - - name = PyUnicode_FromString(modname); - if (name == NULL) goto bad_exit; - - module = PyImport_Import(name); - Py_DECREF(name); - - if (module == NULL) goto no_import; - - dict = PyModule_GetDict(module); - class = PyDict_GetItemString(dict, "AutoLoad"); - - if (class == NULL) goto no_class; - if (!PyType_Check(class->ob_type)) goto no_class; - - instance = PyObject_CallFunction(class, NULL); - if (instance == NULL) goto no_instance; - - result = G_PLUGIN_MODULE(pygobject_get(instance)); - - result->filename = strdup(filename); - - /** - * L'instance Python et l'objet GLib résultante sont un même PyGObject. - * - * Donc pas besoin de toucher au comptage des références ici, la libération - * se réalisera à la fin, quand l'objet GLib sera libéré. - */ - - Py_DECREF(module); - - return result; - - no_instance: - - log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance")); - - no_class: - - if (class == NULL) - log_plugin_simple_message(LMT_ERROR, - _("An error occured when looking for the 'AutoLoad': item not found!")); - - no_import: - - Py_XDECREF(module); - - log_pychrysalide_exception(_("An error occured when importing '%s'"), modname); - - bad_exit: - - return NULL; - -} diff --git a/plugins/pychrysalide/plugins/plugin.h b/plugins/pychrysalide/plugins/plugin.h index ad54b8e..fd3d239 100644 --- a/plugins/pychrysalide/plugins/plugin.h +++ b/plugins/pychrysalide/plugins/plugin.h @@ -27,13 +27,9 @@ #include <Python.h> -#include <glib-object.h> #include <stdbool.h> -#include <plugins/plugin.h> - - /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_plugin_module_type(void); @@ -41,9 +37,6 @@ PyTypeObject *get_python_plugin_module_type(void); /* Prend en charge l'objet 'pychrysalide.plugins.PluginModule'. */ bool ensure_python_plugin_module_is_registered(void); -/* Crée un greffon à partir de code Python. */ -GPluginModule *create_python_plugin(const char *, const char *); - #endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PLUGIN_H */ diff --git a/plugins/pychrysalide/plugins/python-int.h b/plugins/pychrysalide/plugins/python-int.h new file mode 100644 index 0000000..7408d18 --- /dev/null +++ b/plugins/pychrysalide/plugins/python-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python-int.h - prototypes internes pour la déclinaison Python de greffons + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H + + +#include "python.h" + + +#include <plugins/plugin-int.h> + + + +/* Greffon Python pour Chrysalide (instance) */ +struct _GPythonPlugin +{ + GPluginModule parent; /* A laisser en premier */ + + char *file; /* Valeur initiale de __file__ */ + +}; + + +/* Greffon Python pour Chrysalide (classe) */ +struct _GPythonPluginClass +{ + GPluginModuleClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un greffon Python. */ +bool g_python_plugin_create(GPythonPlugin *, const char *, const char *, const char *, const char *, char ***, size_t *, const char *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H */ diff --git a/plugins/pychrysalide/plugins/python.c b/plugins/pychrysalide/plugins/python.c new file mode 100644 index 0000000..d6b9281 --- /dev/null +++ b/plugins/pychrysalide/plugins/python.c @@ -0,0 +1,489 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python.c - déclinaison Python de greffons + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "python.h" + + +#include <malloc.h> +#include <pygobject.h> + + +#include <common/compiler.h> +#include <common/cpp.h> + + +#include "plugin.h" +#include "python-int.h" +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */ + + +/* Initialise la classe des greffons Python. */ +static void g_python_plugin_class_init(GPythonPluginClass *); + +/* Initialise une instance de greffon Python. */ +static void g_python_plugin_init(GPythonPlugin *); + +/* Supprime toutes les références externes. */ +static void g_python_plugin_dispose(GPythonPlugin *); + +/* Procède à la libération totale de la mémoire. */ +static void g_python_plugin_finalize(GPythonPlugin *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Pointe le fichier contenant le greffon manipulé. */ +static char *g_python_plugin_get_filename(const GPythonPlugin *); + +/* Fournit le nom brut associé au greffon. */ +static char *g_python_plugin_get_modname(const GPythonPlugin *); + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(python_plugin, G_TYPE_PYTHON_PLUGIN); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon Python. */ +G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE); + + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_class_init(GPythonPluginClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->get_filename = (get_plugin_filename_fc)g_python_plugin_get_filename; + plugin->get_modname = (get_plugin_modname_fc)g_python_plugin_get_modname; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_init(GPythonPlugin *plugin) +{ + STORE_PLUGIN_ABI(plugin); + + plugin->file = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_dispose(GPythonPlugin *plugin) +{ + G_OBJECT_CLASS(g_python_plugin_parent_class)->dispose(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_finalize(GPythonPlugin *plugin) +{ + if (plugin->file != NULL) + free(plugin->file); + + G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* name = nom du greffon pour référence, principalement. * +* desc = présentation éventuelle à destination humaine. * +* version = indication de version éventuelle. * +* url = référence vers une ressource en ligne. * +* required = liste de dépendances éventuelles ou NULL. * +* count = taille de cette liste. * +* file = emplacement du script considéré. * +* * +* Description : Met en place un greffon Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_python_plugin_create(GPythonPlugin *plugin, const char *name, const char *desc, const char *version, const char *url, char ***required, size_t *count, const char *file) +{ + bool result; /* Bilan à retourner */ + + /* Ajout imposé d'une dépendance à Python */ + + *required = realloc(*required, ++(*count) * sizeof(char *)); + + (*required)[*count - 1] = strdup("PyChrysalide"); + + /* Poursuite de la mise en place */ + + result = g_plugin_module_create(G_PLUGIN_MODULE(plugin), + name, desc, version, url, + CONST_ARRAY_CAST(*required, char), *count); + + if (result) + plugin->file = strdup(file); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Pointe le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_python_plugin_get_filename(const GPythonPlugin *plugin) +{ + char *result; /* Chemin d'accès à renvoyer */ + + result = strdup(plugin->file); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à valider. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_python_plugin_get_modname(const GPythonPlugin *plugin) +{ + char *result; /* Désignation brute à renvoyer*/ + char *filename; /* Chemin du script Python */ + char *name; /* Nom du fichier associé */ + size_t length; /* Taille du nom */ + int ret; /* Bilan d'une comparaison */ + + filename = g_python_plugin_get_filename(plugin); + + name = basename(filename); + + length = strlen(name); + +#define PYTHON_SUFFIX ".py" + + ret = strncmp(&name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)], + PYTHON_SUFFIX, + STATIC_STR_SIZE(PYTHON_SUFFIX)); + + if (ret == 0) + name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)] = '\0'; + + result = strdup(name); + + free(filename); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + const char *version; /* Version du greffon */ + const char *url; /* Site Web associé */ + charp_array_t required; /* Liste des dépendances */ + const char *file; /* Emplacement du script */ + int ret; /* Bilan de lecture des args. */ + PyObject *value; /* Valeur d'un arg. implicite */ + const char *name; /* Désignation humaine courte */ + const char *desc; /* Description plus loquace */ + GPythonPlugin *plugin; /* Greffon à manipuler */ + + static char *kwlist[] = { "file", "version", "url", "required", NULL }; + +#define PYTHON_PLUGIN_DOC \ + "The PythonPlugin class helps to build custom Python plugins:\n" \ + "* some required information (*name* and *desc* for the parent" \ + " constructor) is automatically extracted from the final class name" \ + " or its documentation;\n" \ + "* implementations for pychrysalide.plugins.PluginModule._get_filename()" \ + " and pychrysalide.plugins.PluginModule._get_modname() are provided," \ + " relying on the *file* argument.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect the" \ + " following arguments as keyword parameters:\n" \ + "* *file*: path to the Python script; the value should be equal to the" \ + " *__file__* keyword;\n" \ + "* *version* (optional): a string providing the version of the plugin;" \ + " Version format is free;\n" \ + "* *url* (optional): a string for the homepage describing the plugin;\n" \ + "* *required* (optional): dependencies of the plugin." + + /* Récupération des paramètres */ + + version = NULL; + url = NULL; + required.values = NULL; + required.length = 0; + + ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|ssO&", kwlist, + &file, &version, &url, + convert_to_sequence_to_charp_array, &required); + if (!ret) return -1; + + name = self->ob_type->tp_name; + + value = PyObject_GetAttrString(self, "__doc__"); + + if (value != NULL && PyUnicode_Check(value)) + desc = PyUnicode_AsUTF8(value); + else + desc = NULL; + + Py_XDECREF(value); + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + plugin = G_PYTHON_PLUGIN(pygobject_get(self)); + + STORE_PLUGIN_ABI(plugin); + + if (!g_python_plugin_create(plugin, name, desc, version, url, + &required.values, &required.length, + file)) + { + clean_charp_array(&required); + PyErr_SetString(PyExc_ValueError, _("Unable to create Python plugin.")); + return -1; + } + + clean_charp_array(&required); + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION POUR SUPPORT PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_python_plugin_type(void) +{ + static PyMethodDef py_python_plugin_methods[] = { + { NULL } + }; + + static PyGetSetDef py_python_plugin_getseters[] = { + { NULL } + }; + + static PyTypeObject py_python_plugin_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.PythonPlugin", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = PYTHON_PLUGIN_DOC, + + .tp_methods = py_python_plugin_methods, + .tp_getset = py_python_plugin_getseters, + + .tp_init = py_python_plugin_init, + .tp_new = py_python_plugin_new, + + }; + + return &py_python_plugin_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_python_plugin_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'PythonPlugin' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_python_plugin_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_plugin_module_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type)) + return false; + + } + + return true; + +} diff --git a/plugins/pychrysalide/plugins/python.h b/plugins/pychrysalide/plugins/python.h new file mode 100644 index 0000000..8613bd1 --- /dev/null +++ b/plugins/pychrysalide/plugins/python.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python.h - prototypes pour la déclinaison Python de greffons + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H + + +#include <Python.h> +#include <stdbool.h> + + +#include <glibext/helpers.h> + + + +/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */ + + +#define G_TYPE_PYTHON_PLUGIN (g_python_plugin_get_type()) + +DECLARE_GTYPE(GPythonPlugin, g_python_plugin, G, PYTHON_PLUGIN); + + + +/* ------------------------- DEFINITION POUR SUPPORT PYTHON ------------------------- */ + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_python_plugin_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. */ +bool ensure_python_python_plugin_is_registered(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H */ diff --git a/plugins/pychrysalide/plugins/translate.c b/plugins/pychrysalide/plugins/translate.c deleted file mode 100644 index 055788c..0000000 --- a/plugins/pychrysalide/plugins/translate.c +++ /dev/null @@ -1,110 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * translate.c - conversion de structures liées aux greffons en objets Python - * - * Copyright (C) 2021 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "translate.h" - - -#include <assert.h> - - -#include "plugin.h" -#include "../helpers.h" -#include "../struct.h" - - - -/****************************************************************************** -* * -* Paramètres : iface = ensemble d'informations à transcrire en Python. * -* * -* Description : Traduit une description d'interface de greffon. * -* * -* Retour : Structure mise en place ou NULL en cas d'erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -PyObject *translate_plugin_interface_to_python(const plugin_interface *iface) -{ - PyObject *result; /* Construction à retourner */ - PyTypeObject *base; /* Modèle d'objet à créer */ - bool status; /* Bilan d'une traduction */ - PyObject *array; /* Tableau à insérer */ - size_t i; /* Boucle de parcours */ - PyTypeObject *itype; /* Type d'élément à créer */ - PyObject *item; /* Elément mis en place */ - - base = get_python_py_struct_type(); - - result = PyObject_CallFunction((PyObject *)base, NULL); - assert(result != NULL); - - status = true; - - if (status) status = TRANSLATE_STRING_FIELD(result, iface, gtp_name); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, name); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, desc); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, version); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, url); - - if (status) status = TRANSLATE_BOOLEAN_FIELD(result, iface, container); - - if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, required, &array); - - if (status) - { - itype = get_python_plugin_module_type(); - - for (i = 0; i < iface->required_count; i++) - { - item = PyUnicode_FromString(iface->required[i]); - PyTuple_SetItem(array, i, item); - } - - } - - if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, actions, &array); - - if (status) - { - itype = get_python_plugin_module_type(); - - for (i = 0; i < iface->actions_count; i++) - { - item = cast_with_constants_group_from_type(itype, "PluginAction", iface->actions[i]); - PyTuple_SetItem(array, i, item); - } - - } - - if (!status) - { - Py_DECREF(result); - result = NULL; - } - - return result; - -} diff --git a/plugins/pynb/Makefile.am b/plugins/pynb/Makefile.am new file mode 100644 index 0000000..50e549f --- /dev/null +++ b/plugins/pynb/Makefile.am @@ -0,0 +1,77 @@ + +BUILT_SOURCES = resources.h resources.c + + +lib_LTLIBRARIES = libpynbui.la + +libdir = $(pluginslibdir) + + +# if BUILD_PYTHON3_BINDINGS + +# PYTHON3_LIBADD = python/libpynbpython.la + +# if BUILD_DISCARD_LOCAL + +# PYTHON3_LDFLAGS = -Wl,-rpath,$(pluginslibdir) \ +# -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so + +# else + +# PYTHON3_LDFLAGS = -Wl,-rpath,$(abs_top_srcdir)/plugins/pychrysalide/.libs \ +# -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so + +# endif + +# PYTHON3_SUBDIRS = python + +# endif + + +libpynbui_la_SOURCES = \ + core-ui-int.h \ + core-ui.h core-ui.c \ + panel-int.h \ + panel.h panel.c \ + params-int.h \ + params.h params.c \ + prefs-int.h \ + prefs.h prefs.c \ + resources.h resources.c + +libpynbui_la_LIBADD = \ + $(PYTHON3_LIBADD) + +libpynbui_la_CFLAGS = $(LIBGTK4_CFLAGS) + +libpynbui_la_LDFLAGS = \ + $(LIBGTK4_LIBS) $(PYTHON3_LDFLAGS) + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libpynbui_la_SOURCES:%c=) + + +RES_FILES = \ + panel.ui \ + params.ui \ + prefs.ui \ + data/images/pynb-symbolic.svg + + +resources.c: gresource.xml $(RES_FILES) + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name plugins_pynb gresource.xml + +resources.h: gresource.xml + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name plugins_pynb gresource.xml + + +CLEANFILES = resources.h resources.c + +EXTRA_DIST = gresource.xml $(RES_FILES) + + +AM_CPPFLAGS = -I$(top_srcdir)/src $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) + +SUBDIRS = $(PYTHON3_SUBDIRS) diff --git a/plugins/pynb/core-ui-int.h b/plugins/pynb/core-ui-int.h new file mode 100644 index 0000000..caf5713 --- /dev/null +++ b/plugins/pynb/core-ui-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui-int.h - prototypes internes pour le plugin présentant des notes avec code Python + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYNB_CORE_UI_INT_H +#define _PLUGINS_PYNB_CORE_UI_INT_H + + +#include <plugins/native-int.h> + + +#include "core-ui.h" + + + +/* Greffon natif pour la présentation de notes avec texte et code Python (instance) */ +struct _GPythonNotebookPluginUI +{ + GNativePlugin parent; /* A laisser en premier */ + +}; + + +/* Greffon natif pour la présentation de notes avec texte et code Python (classe) */ +struct _GPythonNotebookPluginUIClass +{ + GNativePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un module pour présentation. */ +bool g_python_notebook_plugin_ui_create(GPythonNotebookPluginUI *, GModule *); + + + +#endif /* _PLUGINS_PYNB_CORE_UI_INT_H */ diff --git a/plugins/pynb/core-ui.c b/plugins/pynb/core-ui.c new file mode 100644 index 0000000..dd49a83 --- /dev/null +++ b/plugins/pynb/core-ui.c @@ -0,0 +1,363 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.c - présentation de notes sous forme de texte et de code Python + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "core-ui.h" + + +#include <i18n.h> +#include <gui/core/panels.h> +#include <plugins/self.h> +#include <plugins/tweakable-int.h> + + +#include "core-ui-int.h" +#include "panel.h" +#include "params.h" +#include "prefs.h" + + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +/* Initialise la classe des recherches et identifications. */ +static void g_python_notebook_plugin_ui_class_init(GPythonNotebookPluginUIClass *); + +/* Procède à l'initialisation de l'interface d'intervention. */ +static void g_python_notebook_plugin_ui_tweakable_plugin_interface_init(GTweakablePluginInterface *); + +/* Initialise une instance de recherches et identifications. */ +static void g_python_notebook_plugin_ui_init(GPythonNotebookPluginUI *); + +/* Supprime toutes les références externes. */ +static void g_python_notebook_plugin_ui_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_python_notebook_plugin_ui_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_python_notebook_plugin_ui_enable(GPythonNotebookPluginUI *); + +/* Prend acte de la désactivation du greffon. */ +static bool g_python_notebook_plugin_ui_disable(GPythonNotebookPluginUI *); + + + +/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */ + + +/* Fournit une liste de sections de configuration. */ +static tweak_info_t *g_python_notebook_plugin_ui_get_tweak_info(const GTweakablePlugin *, size_t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une présentation de notes texte et code Python. */ +G_DEFINE_TYPE_WITH_CODE(GPythonNotebookPluginUI, g_python_notebook_plugin_ui, G_TYPE_NATIVE_PLUGIN, + G_IMPLEMENT_INTERFACE(G_TYPE_TWEAKABLE_PLUGIN, g_python_notebook_plugin_ui_tweakable_plugin_interface_init)); + + +NATIVE_PLUGIN_ENTRYPOINT(g_python_notebook_plugin_ui_new); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des recherches et identifications. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_class_init(GPythonNotebookPluginUIClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = g_python_notebook_plugin_ui_dispose; + object->finalize = g_python_notebook_plugin_ui_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = (pg_management_fc)g_python_notebook_plugin_ui_enable; + plugin->disable = (pg_management_fc)g_python_notebook_plugin_ui_disable; + +} + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'intervention. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_tweakable_plugin_interface_init(GTweakablePluginInterface *iface) +{ + iface->get_info = g_python_notebook_plugin_ui_get_tweak_info; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de recherches et identifications. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_init(GPythonNotebookPluginUI *plugin) +{ + STORE_PLUGIN_ABI(plugin); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_dispose(GObject *object) +{ + G_OBJECT_CLASS(g_python_notebook_plugin_ui_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_finalize(GObject *object) +{ + G_OBJECT_CLASS(g_python_notebook_plugin_ui_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : module = extension vue du système. * +* * +* Description : Crée un module pour présentation de notes. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +GPluginModule *g_python_notebook_plugin_ui_new(GModule *module) +{ + GPythonNotebookPluginUI *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_PYTHON_NOTEBOOK_PLUGIN_UI, NULL); + + if (!g_python_notebook_plugin_ui_create(result, module)) + g_clear_object(&result); + + return G_PLUGIN_MODULE(result); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = extension vue du système. * +* * +* Description : Met en place un module pour un module pour présentation. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_python_notebook_plugin_ui_create(GPythonNotebookPluginUI *plugin, GModule *module) +{ + bool result; /* Bilan à retourner */ + +#ifdef INCLUDE_PYTHON3_BINDINGS +# define PG_REQ REQ_LIST("PyChrysalide") +#else +# define PG_REQ NO_REQ +#endif + + result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin), + "PythonNotebook", + "Edit notebook with text and code to support binary analysis", + PACKAGE_VERSION, + CHRYSALIDE_WEBSITE("doc/plugins/pynb"), + PG_REQ, + module); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de l'activation du greffon. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_python_notebook_plugin_ui_enable(GPythonNotebookPluginUI *plugin) +{ + bool result; /* Bilan à retourner */ + panel_info_t info; /* Infos d'enregistrement */ + + info.category = "Main"; + + info.image = "pynb-symbolic"; + info.title = _("Python notebook"); + info.desc = _("Edit notebook with text and code to support binary analysis"); + + info.personality = FPP_MAIN_PANEL; + + info.panel_type = GTK_TYPE_PYTHON_NOTEBOOK_PANEL; + info.params_type = GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS; + + result = register_framework_panel_definition(&info); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de la désactivation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_python_notebook_plugin_ui_disable(GPythonNotebookPluginUI *plugin) +{ + bool result; /* Bilan à retourner */ + + + // TODO : unregister + + result = true; + + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* INTEGRATION DANS L'EDITION DES PREFERENCES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = interface à manipuler. * +* count = taille de la liste renvoyée. [OUT] * +* * +* Description : Fournit une liste de sections de configuration. * +* * +* Retour : Définition(s) de section de configuration ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static tweak_info_t *g_python_notebook_plugin_ui_get_tweak_info(const GTweakablePlugin *plugin, size_t *count) +{ + tweak_info_t *result; /* Liste à renvoyer */ + + tweak_info_t infos[] = { + TWEAK_SIMPLE_DEF("root", "Basics", + "pynb-symbolic", "pynotebook", "Notebook", GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL), + }; + + *count = 1; + result = malloc(*count * sizeof(tweak_info_t)); + + memcpy(result, infos, *count * sizeof(tweak_info_t)); + + return result; + +} diff --git a/plugins/pynb/core-ui.h b/plugins/pynb/core-ui.h new file mode 100644 index 0000000..8ab9dd9 --- /dev/null +++ b/plugins/pynb/core-ui.h @@ -0,0 +1,43 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.h - prototypes pour la présentation de notes sous forme de texte et de code Python + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_FIDO_CORE_UI_H +#define _PLUGINS_FIDO_CORE_UI_H + + +#include <plugins/plugin.h> +#include <plugins/plugin-int.h> + + + +#define G_TYPE_PYTHON_NOTEBOOK_PLUGIN_UI (g_python_notebook_plugin_ui_get_type()) + +DECLARE_GTYPE(GPythonNotebookPluginUI, g_python_notebook_plugin_ui, G, PYTHON_NOTEBOOK_PLUGIN_UI); + + +/* Crée un module pour présentation de notes. */ +GPluginModule *g_python_notebook_plugin_ui_new(GModule *); + + + +#endif /* _PLUGINS_FIDO_CORE_UI_H */ diff --git a/plugins/pynb/data/images/pynb-symbolic.svg b/plugins/pynb/data/images/pynb-symbolic.svg new file mode 100644 index 0000000..f8cae60 --- /dev/null +++ b/plugins/pynb/data/images/pynb-symbolic.svg @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="256" + height="256" + viewBox="0 0 67.73333 67.733333" + version="1.1" + id="svg2759" + sodipodi:docname="pynotebook.svg" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview2761" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="false" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="false" + inkscape:zoom="2.9329997" + inkscape:cx="126.83261" + inkscape:cy="128.1964" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" /> + <defs + id="defs2756"> + <linearGradient + id="b" + x1="28.809" + y1="28.882" + x2="45.803001" + y2="45.162998" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(12.026515,0,0,11.963752,-37.015053,-73.520624)"> + <stop + stop-color="#FFE052" + id="stop3586" /> + <stop + offset="1" + stop-color="#FFC331" + id="stop3588" /> + </linearGradient> + <linearGradient + id="a" + x1="19.075001" + y1="18.782" + x2="34.897999" + y2="34.658001" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(12.026515,0,0,11.963752,-37.015053,-73.520624)"> + <stop + stop-color="#387EB8" + id="stop3581" /> + <stop + offset="1" + stop-color="#366994" + id="stop3583" /> + </linearGradient> + </defs> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g3524" + transform="translate(2.9562937,-0.62320885)" + sodipodi:insensitive="true" + style="display:none"> + <path + id="rect2966" + style="fill:#3a1616;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 49.272443,10.836031 c 0.102978,0.351405 0.158647,0.723392 0.158647,1.108976 V 57.03476 c 0,2.168007 -1.745406,3.913456 -3.913456,3.913456 H 16.30288 c -0.385538,0 -0.75709,-0.05622 -1.108459,-0.159163 0.237938,0.812336 0.730853,1.513585 1.387112,2.01175 0.65626,0.498165 1.475864,0.793246 2.367181,0.793246 h 29.214753 c 2.16805,0 3.913456,-1.745449 3.913456,-3.913456 V 14.59084 c 0,-1.782422 -1.180022,-3.278791 -2.80448,-3.754809 z" + sodipodi:nodetypes="csssscsssssc" /> + <path + id="rect2964" + style="display:inline;fill:#a44040;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 13.657273,5.3857002 h 29.21476 c 2.168053,0 3.913452,1.7453645 3.913452,3.9133733 V 54.388991 c 0,2.168009 -1.745399,3.913373 -3.913452,3.913373 h -29.21476 c -1.084026,0 -2.062389,-0.436341 -2.769745,-1.143683 -0.707357,-0.707342 -1.1437069,-1.685685 -1.1437069,-2.76969 V 9.2990735 c 0,-2.1680088 1.7453999,-3.9133733 3.9134519,-3.9133733 z" + sodipodi:nodetypes="ssssssssss" /> + <path + id="rect3518" + style="fill:#2d3a16;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 10.887528,57.158681 5.291742,4.9e-5 5.29299,5.29246 h -5.291667 z" + sodipodi:nodetypes="ccccc" /> + </g> + <g + id="g3807" + transform="matrix(0.275,0,0,0.27644269,10.179141,20.324238)" + style="display:none;stroke-width:3.62685"> + <path + d="m 76.135321,-13.223349 c -26.868437,0 -25.19164,11.6515879 -25.19164,11.6515879 l 0.03307,12.0716141 h 25.638125 v 3.621485 h -35.82789 c 0,0 -17.187994,-1.951302 -17.187994,25.161874 0,27.106562 15.005182,26.147447 15.005182,26.147447 h 8.956145 V 52.849722 c 0,0 -0.482864,-15.005182 14.767057,-15.005182 h 25.426458 c 0,0 14.287506,0.231511 14.287506,-13.807942 V 0.82271803 c 0,0 2.16958,-14.04606703 -25.906021,-14.04606703 z m -14.138672,8.1160932 c 2.549922,0 4.613672,2.0637499 4.613672,4.61367177 0,2.54992183 -2.06375,4.61367183 -4.613672,4.61367183 a 4.6070572,4.6070572 0 0 1 -4.613672,-4.61367183 c 0,-2.54992187 2.06375,-4.61367177 4.613672,-4.61367177 z" + fill="url(#a)" + id="path3577" + style="fill:url(#a);stroke-width:11.9951" /> + <path + d="m 76.895998,92.057664 c 26.868432,0 25.191642,-11.651588 25.191642,-11.651588 l -0.0331,-12.071615 h -25.6381 v -3.621484 h 35.82458 c 0,0 17.1913,1.951302 17.1913,-25.158567 0,-27.109869 -15.00518,-26.147447 -15.00518,-26.147447 H 105.471 v 12.57763 c 0,0 0.48286,15.005182 -14.76706,15.005182 H 65.277482 c 0,0 -14.287499,-0.231511 -14.287499,13.807942 v 23.21388 c 0,0 -2.169584,14.046067 25.906015,14.046067 z m 14.138671,-8.116093 a 4.6070572,4.6070572 0 0 1 -4.613671,-4.613672 c 0,-2.546615 2.06375,-4.610365 4.613671,-4.610365 2.549922,0 4.613672,2.060443 4.613672,4.610365 0,2.553229 -2.06375,4.613672 -4.613672,4.613672 z" + fill="url(#b)" + id="path3579" + style="fill:url(#b);stroke-width:11.9951" /> + </g> + <g + id="g3859" + style="display:none;fill:#000000;fill-opacity:1"> + <path + id="path3544" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 52.228737,10.212822 c 0.102978,0.351405 0.158647,0.723392 0.158647,1.108976 v 45.089753 c 0,2.168007 -1.745406,3.913456 -3.913456,3.913456 H 19.259174 c -0.385538,0 -0.75709,-0.05622 -1.108459,-0.159163 0.237938,0.812336 0.730853,1.513585 1.387112,2.01175 0.65626,0.498165 1.475864,0.793246 2.367181,0.793246 h 29.214753 c 2.16805,0 3.913456,-1.745449 3.913456,-3.913456 V 13.967631 c 0,-1.782422 -1.180022,-3.278791 -2.80448,-3.754809 z" + sodipodi:nodetypes="csssscsssssc" /> + <path + id="path3546" + style="display:inline;fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 16.613456,4.7624999 C 14.445408,4.7625 12.7,6.5079516 12.7,8.675956 v 45.089753 c 0,1.084003 0.436244,2.062515 1.143599,2.769857 0.707354,0.70734 1.685833,1.143599 2.769857,1.143599 h 29.214753 c 2.168049,0 3.913456,-1.745451 3.913456,-3.913456 V 8.675956 c 0,-2.1680044 -1.745407,-3.9134561 -3.913456,-3.9134561 z M 31.116446,16.66875 c 7.720775,0 7.124113,3.882967 7.124113,3.882967 v 6.417179 c 0,3.881096 -3.928959,3.817338 -3.928959,3.817338 h -6.992338 c -4.19372,0 -4.061251,4.148067 -4.061251,4.148067 v 3.477824 h -2.462898 c 0,0 -4.126363,0.264897 -4.126363,-7.2285 0,-7.495223 4.726843,-6.955647 4.726843,-6.955647 h 9.852628 v -1.000973 h -7.050215 l -0.0093,-3.337264 c 0,0 -0.461064,-3.220991 6.927742,-3.220991 z m -3.888135,2.24379 c -0.701226,0 -1.268656,0.570469 -1.268656,1.275374 a 1.2669408,1.2735872 0 0 0 1.268656,1.275375 c 0.701229,0 1.268657,-0.570467 1.268657,-1.275375 0,-0.704905 -0.567428,-1.275374 -1.268657,-1.275374 z m 14.46475,5.117 c 0.453342,-0.0018 4.079854,0.203094 4.079854,7.229016 0,7.494318 -4.727359,6.955131 -4.727359,6.955131 h -9.852112 v 1.000973 h 7.050732 l 0.0093,3.337263 c 0,0 0.461069,3.220992 -6.927742,3.220992 -7.720783,0 -7.124113,-3.882967 -7.124113,-3.882967 v -6.417179 c 0,-3.881101 3.928959,-3.817338 3.928959,-3.817338 h 6.992338 c 4.193725,0 4.060734,-4.148067 4.060734,-4.148067 v -3.47679 h 2.462898 c 0,0 0.01629,-9.15e-4 0.04651,-0.001 z M 35.21387,40.97941 c -0.701227,0 -1.269173,0.570348 -1.269173,1.27434 a 1.2669408,1.2735872 0 0 0 1.269173,1.275375 c 0.701228,0 1.268657,-0.569554 1.268657,-1.275375 0,-0.704906 -0.567429,-1.27434 -1.268657,-1.27434 z" /> + <path + id="path3548" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 13.843822,56.535472 5.291742,4.9e-5 5.29299,5.29246 h -5.291667 z" + sodipodi:nodetypes="ccccc" /> + </g> + <path + id="rect3934" + style="display:none;fill:#00ba2b;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 4.7624999,62.970832 20.637499,4.7624999 h 42.333333 l -15.875,58.2083321 z" + sodipodi:nodetypes="ccccc" /> + <path + id="path4924" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 24.551 4.7625 C 22.3829 4.7625 20.1615 6.50795 19.5702 8.67596 L 7.27299 53.7657 C 6.97735 54.8497 7.14673 55.8282 7.66117 56.5356 L 11.5111 61.8277 L 11.5147 61.8277 C 11.6056 61.9525 11.7049 62.0706 11.8167 62.1776 C 12.3371 62.6758 13.0763 62.9708 13.9676 62.9708 L 43.1824 62.9708 C 45.3504 62.9708 47.5719 61.2254 48.1631 59.0574 L 60.4603 13.9676 C 60.9465 12.1852 60.1745 10.6888 58.6799 10.2128 C 58.687 10.5642 58.6413 10.9362 58.5361 11.3218 L 46.2389 56.4115 C 45.6476 58.5795 43.4262 60.325 41.2581 60.325 L 15.7098 60.325 L 13.7851 57.6792 L 39.3339 57.6792 C 41.5019 57.6792 43.7234 55.9337 44.3147 53.7657 L 56.6119 8.67596 C 57.2031 6.50795 55.9338 4.7625 53.7657 4.7625 L 24.551 4.7625 Z M 35.8068 16.6688 C 43.5276 16.6688 41.8719 20.5517 41.8719 20.5517 L 40.1218 26.9689 C 39.0633 30.85 35.1517 30.7862 35.1517 30.7862 L 28.1594 30.7862 C 23.9657 30.7862 22.9668 34.9343 22.9668 34.9343 L 22.0183 38.4121 L 19.5554 38.4121 C 19.5554 38.4121 15.3568 38.677 17.4005 31.1836 C 19.4446 23.6884 24.0243 24.228 24.0243 24.228 L 33.877 24.228 L 34.1499 23.227 L 27.0997 23.227 L 28.0006 19.8897 C 28.0006 19.8897 28.418 16.6688 35.8068 16.6688 Z M 31.3067 18.9125 C 30.6055 18.9125 29.8825 19.483 29.6902 20.1879 C 29.5975 20.5263 29.6424 20.851 29.8152 21.0902 C 29.9879 21.3295 30.2743 21.4637 30.611 21.4633 C 31.3123 21.4633 32.0353 20.8928 32.2275 20.1879 C 32.4198 19.483 32.0079 18.9125 31.3067 18.9125 Z M 44.3759 24.0295 C 44.8297 24.0277 48.4004 24.2326 46.4842 31.2586 C 44.4403 38.7529 39.86 38.2137 39.86 38.2137 L 30.0079 38.2137 L 29.7349 39.2147 L 36.7856 39.2147 L 35.8848 42.5519 C 35.8848 42.5519 35.4674 45.7729 28.0786 45.7729 C 20.3578 45.7729 22.0135 41.8899 22.0135 41.8899 L 23.7636 35.4728 C 24.8221 31.5917 28.7336 31.6554 28.7336 31.6554 L 35.726 31.6554 C 39.9197 31.6554 40.918 27.5074 40.918 27.5074 L 41.8662 24.0306 L 44.3291 24.0306 C 44.3291 24.0306 44.3457 24.0296 44.3759 24.0295 Z M 33.274 40.9794 C 32.5728 40.9794 31.8493 41.5498 31.6573 42.2538 C 31.5645 42.5922 31.6095 42.917 31.7824 43.1563 C 31.9553 43.3956 32.2418 43.5297 32.5787 43.5291 C 33.2799 43.5291 34.0026 42.9596 34.1951 42.2538 C 34.3874 41.5488 33.9753 40.9794 33.274 40.9794 Z" /> + <path + style="display:none;fill:#af00ba;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 4.7624999,62.970832 20.637499,4.7624999 h 42.333333 l -15.875,58.2083321 z" + id="path5158" + sodipodi:nodetypes="ccccc" /> + </g> +</svg> diff --git a/plugins/pynb/gresource.xml b/plugins/pynb/gresource.xml new file mode 100644 index 0000000..71eb8ed --- /dev/null +++ b/plugins/pynb/gresource.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/re/chrysalide/framework/gui/panels"> + <file compressed="true" alias="pynb-panel.ui">panel.ui</file> + <file compressed="true" alias="pynb-params.ui">params.ui</file> + <file compressed="true" alias="pynb-prefs.ui">prefs.ui</file> + </gresource> + <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions"> + <file compressed="true" alias="pynb-symbolic.svg">data/images/pynb-symbolic.svg</file> + </gresource> +</gresources> diff --git a/plugins/pynb/panel-int.h b/plugins/pynb/panel-int.h new file mode 100644 index 0000000..23884c7 --- /dev/null +++ b/plugins/pynb/panel-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel-int.h - prototypes internes pour le panneau dédié à la présentation de notes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYNB_PANEL_INT_H +#define _PLUGINS_PYNB_PANEL_INT_H + + +#include <gtkext/panel-int.h> + + +#include "panel.h" + + + +/* Panneau de cartopgraphie des dispositions d'échantillons (instance) */ +struct _GtkPythonNotebookPanel +{ + GtkTiledPanel parent; /* A laisser en premier */ + +}; + +/* Panneau de cartopgraphie des dispositions d'échantillons (classe) */ +struct _GtkPythonNotebookPanelClass +{ + GtkTiledPanelClass parent; /* A laisser en premier */ + +}; + + +/* Met en place nouvelle instance de panneau de présentation. */ +bool gtk_python_notebook_panel_create(GtkPythonNotebookPanel *); + + + +#endif /* _PLUGINS_PYNB_PANEL_INT_H */ diff --git a/plugins/pynb/panel.c b/plugins/pynb/panel.c new file mode 100644 index 0000000..8ce9cdb --- /dev/null +++ b/plugins/pynb/panel.c @@ -0,0 +1,210 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel.c - panneau dédié à la présentation de notes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "panel.h" + + +#include <gtkext/helpers.h> + + +#include "panel-int.h" + + + +/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ + + +/* Initialise la classe des panneaux pour binaires. */ +static void gtk_python_notebook_panel_class_init(GtkPythonNotebookPanelClass *); + +/* Initialise une instance de panneau pour binaire. */ +static void gtk_python_notebook_panel_init(GtkPythonNotebookPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_python_notebook_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_python_notebook_panel_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Fournit les composants adaptés pour la barre de titre. */ +//static GListStore *gtk_python_notebook_panel_get_title_widgets(GtkTiledPanel *, bool); + + + +/* ---------------------------------------------------------------------------------- */ +/* COEUR D'UN PANNEAU D'AFFICHAGE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un panneau de présentation de notes. */ +G_DEFINE_TYPE(GtkPythonNotebookPanel, gtk_python_notebook_panel, GTK_TYPE_TILED_PANEL); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des panneaux pour binaires. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_panel_class_init(GtkPythonNotebookPanelClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + GtkTiledPanelClass *panel; /* Version parente de classe */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_python_notebook_panel_dispose; + object->finalize = gtk_python_notebook_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-panel.ui"); + + //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookPanel, summary); + + panel = GTK_TILED_PANEL_CLASS(class); + + //panel->get_widgets = (get_tiled_panel_widgets_cb)gtk_python_notebook_panel_get_title_widgets; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance à initialiser. * +* * +* Description : Initialise une instance de panneau pour binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_panel_init(GtkPythonNotebookPanel *panel) +{ + gtk_widget_init_template(GTK_WIDGET(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_panel_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_PANEL); + + G_OBJECT_CLASS(gtk_python_notebook_panel_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_panel_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_python_notebook_panel_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée une nouvelle instance de panneau de présentation. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkTiledPanel *gtk_python_notebook_panel_new(void) +{ + GtkTiledPanel *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_PYTHON_NOTEBOOK_PANEL, NULL); + + if (!gtk_python_notebook_panel_create(GTK_PYTHON_NOTEBOOK_PANEL(result))) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau de recherche et récupération à remplir. * +* * +* Description : Met en place nouvelle instance de panneau de présentation. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_python_notebook_panel_create(GtkPythonNotebookPanel *panel) +{ + bool result; /* Bilan à retourner */ + + result = true; + + return result; + +} diff --git a/plugins/pynb/panel.h b/plugins/pynb/panel.h new file mode 100644 index 0000000..f9f8016 --- /dev/null +++ b/plugins/pynb/panel.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel.h - prototypes pour le panneau dédié à la présentation de notes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYNB_PANEL_H +#define _PLUGINS_PYNB_PANEL_H + + +#include <gtk/gtk.h> + + +#include <glibext/helpers.h> +#include <gtkext/panel.h> + + + +#define GTK_TYPE_PYTHON_NOTEBOOK_PANEL (gtk_python_notebook_panel_get_type()) + +DECLARE_GTYPE(GtkPythonNotebookPanel, gtk_python_notebook_panel, GTK, PYTHON_NOTEBOOK_PANEL); + + +/* Crée une nouvelle instance de panneau de présentation. */ +GtkTiledPanel *gtk_python_notebook_panel_new(void); + + + +#endif /* _PLUGINS_PYNB_PANEL_H */ diff --git a/plugins/pynb/panel.ui b/plugins/pynb/panel.ui new file mode 100644 index 0000000..d16af80 --- /dev/null +++ b/plugins/pynb/panel.ui @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkPythonNotebookPanel" parent="GtkTiledPanel"> + <child> + <object class="GtkScrolledWindow"> + <property name="hscrollbar-policy">automatic</property> + <property name="vscrollbar-policy">automatic</property> + <property name="hexpand">true</property> + <property name="vexpand">true</property> + <property name="has-frame">true</property> + <property name="margin-start">8</property> + <property name="margin-top">8</property> + <property name="margin-end">8</property> + <property name="margin-bottom">8</property> + + </object> + </child> + </template> + +</interface> diff --git a/plugins/pynb/params-int.h b/plugins/pynb/params-int.h new file mode 100644 index 0000000..7f3bc8b --- /dev/null +++ b/plugins/pynb/params-int.h @@ -0,0 +1,50 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * params-int.h - définitions internes pour l'édition des paramètres initiaux d'une présentation de notes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYNB_PARAMS_INT_H +#define _PLUGINS_PYNB_PARAMS_INT_H + + +#include "params.h" + + + +/* Composant pour les paramètres de chargement d'un binaire (instance) */ +struct _GtkPythonNotebookParameters +{ + GtkGrid parent; /* A laisser en premier */ + + //GtkEntry *filename; /* CHemin d'un binaire */ + +}; + +/* Composant pour les paramètres de chargement d'un binaire (classe) */ +struct _GtkPythonNotebookParametersClass +{ + GtkGridClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _PLUGINS_PYNB_PARAMS_INT_H */ diff --git a/plugins/pynb/params.c b/plugins/pynb/params.c new file mode 100644 index 0000000..690ff95 --- /dev/null +++ b/plugins/pynb/params.c @@ -0,0 +1,171 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * params.c - édition des paramètres initiaux d'une présentation de notes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "params.h" + + +#include "panel.h" +#include "params-int.h" +#include <gtkext/helpers.h> +#include <gui/window.h> + + + +/* Initialise la classe des composants d'édition de paramètres. */ +static void gtk_python_notebook_parameters_class_init(GtkPythonNotebookParametersClass *); + +/* Initialise une instance de composant d'édition de paramètres. */ +static void gtk_python_notebook_parameters_init(GtkPythonNotebookParameters *); + +/* Supprime toutes les références externes. */ +static void gtk_python_notebook_parameters_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_python_notebook_parameters_finalize(GObject *); + +/* Réagit à une demande de création de notes. */ +static void gtk_python_notebook_parameters_on_create_clicked(GtkButton *, GtkPythonNotebookParameters *); + + + +/* Indique le type du composant d'édition des paramètres de chargement. */ +G_DEFINE_TYPE(GtkPythonNotebookParameters, gtk_python_notebook_parameters, GTK_TYPE_GRID); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des composants d'édition de paramètres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_class_init(GtkPythonNotebookParametersClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_python_notebook_parameters_dispose; + object->finalize = gtk_python_notebook_parameters_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-params.ui"); + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_python_notebook_parameters_on_create_clicked)); + + //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookParameters, filename); + +} + + +/****************************************************************************** +* * +* Paramètres : params = composant GTK à initialiser. * +* * +* Description : Initialise une instance de composant d'édition de paramètres.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_init(GtkPythonNotebookParameters *params) +{ + gtk_widget_init_template(GTK_WIDGET(params)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS); + + G_OBJECT_CLASS(gtk_python_notebook_parameters_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_python_notebook_parameters_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton GTK concerné par l'appel. * +* params = paramètres du panneau à mettre en place. * +* * +* Description : Réagit à une demande de création de notes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_on_create_clicked(GtkButton *button, GtkPythonNotebookParameters *params) +{ + GtkRoot *root; /* Racine du composant */ + GtkTiledPanel *tiled; /* Panneau d'affichage complet */ + + root = gtk_widget_get_root(GTK_WIDGET(button)); + + tiled = gtk_python_notebook_panel_new(); + + gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled); + +} diff --git a/plugins/pynb/params.h b/plugins/pynb/params.h new file mode 100644 index 0000000..2d4b39b --- /dev/null +++ b/plugins/pynb/params.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * params.h - prototypes pour l'édition des paramètres initiaux d'une présentation de notes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYNB_PARAMS_H +#define _PLUGINS_PYNB_PARAMS_H + + +#include <gtk/gtk.h> + + +#include <glibext/helpers.h> + + + +#define GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS (gtk_python_notebook_parameters_get_type()) + +DECLARE_GTYPE(GtkPythonNotebookParameters, gtk_python_notebook_parameters, GTK, PYTHON_NOTEBOOK_PARAMETERS); + + + +#endif /* _PLUGINS_PYNB_PARAMS_H */ diff --git a/plugins/pynb/params.ui b/plugins/pynb/params.ui new file mode 100644 index 0000000..21a4788 --- /dev/null +++ b/plugins/pynb/params.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkPythonNotebookParameters" parent="GtkGrid"> + <property name="margin-bottom">12</property> + <property name="margin-end">12</property> + <property name="margin-start">12</property> + <property name="margin-top">12</property> + <property name="column-spacing">12</property> + <property name="row-spacing">8</property> + + <child> + <object class="GtkLabel"> + <property name="label">Load and analyze a new notebook file:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + + <child> + <object class="GtkEntry" id="filename"> + <property name="secondary-icon-name">document-open-symbolic</property> + <property name="placeholder-text">File location</property> + <property name="hexpand">TRUE</property> + <property name="hexpand-set">TRUE</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + <style> + <class name="background"/> + </style> + </object> + </child> + + <child> + <object class="GtkButton" id="create"> + <property name="label">Create</property> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> + <signal name="clicked" handler="gtk_python_notebook_parameters_on_create_clicked"/> + </object> + </child> + + </template> + +</interface> diff --git a/plugins/pynb/prefs-int.h b/plugins/pynb/prefs-int.h new file mode 100644 index 0000000..9c44c68 --- /dev/null +++ b/plugins/pynb/prefs-int.h @@ -0,0 +1,48 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * prefs-int.h - définitions internes pour la configuration des paramètres liés aux notes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYNB_PREFS_INT_H +#define _PLUGINS_PYNB_PREFS_INT_H + + +#include "prefs.h" + + + +/* Composant d'édition des paramètres liés aux notes (instance) */ +struct _GtkPythonNotebookTweakPanel +{ + GtkBox parent; /* A laisser en premier */ + +}; + +/* Composant d'édition des paramètres liés aux notes (classe) */ +struct _GtkPythonNotebookTweakPanelClass +{ + GtkBoxClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _PLUGINS_PYNB_PREFS_INT_H */ diff --git a/src/format/executable-int.c b/plugins/pynb/prefs.c index 0e94e74..9363e9b 100644 --- a/src/format/executable-int.c +++ b/plugins/pynb/prefs.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * executable-int.c - code utile aux formats d'exécutables + * prefs.c - configuration des paramètres liés aux notes * - * Copyright (C) 2015-2018 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,135 +21,123 @@ */ -#include "executable-int.h" +#include "prefs.h" +#include <gtkext/helpers.h> + + +#include "prefs-int.h" + + + +/* Procède à l'initialisation de classe des configurations. */ +static void gtk_python_notebook_tweak_panel_class_init(GtkPythonNotebookTweakPanelClass *); + +/* Procède à l'initialisation des configurations de sécurité. */ +static void gtk_python_notebook_tweak_panel_init(GtkPythonNotebookTweakPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_python_notebook_tweak_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_python_notebook_tweak_panel_finalize(GObject *); + + + +/* Indique le type du composant de configuration des notes. */ +G_DEFINE_TYPE(GtkPythonNotebookTweakPanel, gtk_python_notebook_tweak_panel, GTK_TYPE_BOX); + /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* off = position physique à retrouver. * -* pos = position correspondante. [OUT] * +* Paramètres : class = classe GTK à initialiser. * * * -* Description : Fournit l'emplacement correspondant à une position physique. * +* Description : Procède à l'initialisation de classe des configurations. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -bool g_exe_format_without_virt_translate_offset_into_vmpa(const GExeFormat *format, phys_t off, vmpa2t *pos) +static void gtk_python_notebook_tweak_panel_class_init(GtkPythonNotebookTweakPanelClass *class) { - init_vmpa(pos, off, VMPA_NO_VIRTUAL); + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_python_notebook_tweak_panel_dispose; + object->finalize = gtk_python_notebook_tweak_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-prefs.ui"); - return true; + /* Stockage sécurisé */ + + //gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_python_notebook_tweak_panel_on_new_passwords_changed)); + + //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookTweakPanel, current_primary_passwd); } /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* addr = adresse virtuelle à retrouver. * -* pos = position correspondante. [OUT] * +* Paramètres : panel = composant GTK à initialiser. * * * -* Description : Fournit l'emplacement correspondant à une adresse virtuelle. * +* Description : Procède à l'initialisation des configurations de sécurité. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -bool g_exe_format_without_virt_translate_address_into_vmpa(const GExeFormat *format, virt_t addr, vmpa2t *pos) +static void gtk_python_notebook_tweak_panel_init(GtkPythonNotebookTweakPanel *panel) { - /** - * S'il n'y a pas de notion de mémoire virtuelle, positions physiques et - * adresses virtuelles se confondent. - * - * On reste néanmoins cohérent, et on n'utilise donc pas d'adresse virtuelle. - * - * Les sauts dans le code renvoient de façon transparente vers des positions - * physiques. - */ - - init_vmpa(pos, addr, VMPA_NO_VIRTUAL); - - return true; + gtk_widget_init_template(GTK_WIDGET(panel)); } /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* off = position physique à retrouver. * -* pos = position correspondante. [OUT] * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Fournit l'emplacement correspondant à une position physique. * +* Description : Supprime toutes les références externes. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -bool g_exe_format_translate_offset_into_vmpa_using_portions(GExeFormat *format, phys_t off, vmpa2t *pos) +static void gtk_python_notebook_tweak_panel_dispose(GObject *object) { - bool result; /* Bilan à retourner */ - GBinPortion *portions; /* Liste de découpages */ - - portions = g_exe_format_get_portions(format); - - if (portions == NULL) - result = false; - - else - { - result = g_binary_portion_translate_offset_into_vmpa(portions, off, pos); - - g_object_unref(G_OBJECT(portions)); + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL); - } - - return result; + G_OBJECT_CLASS(gtk_python_notebook_tweak_panel_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* addr = adresse virtuelle à retrouver. * -* pos = position correspondante. [OUT] * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Fournit l'emplacement correspondant à une adresse virtuelle. * +* Description : Procède à la libération totale de la mémoire. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -bool g_exe_format_translate_address_into_vmpa_using_portions(GExeFormat *format, virt_t addr, vmpa2t *pos) +static void gtk_python_notebook_tweak_panel_finalize(GObject *object) { - bool result; /* Bilan à retourner */ - GBinPortion *portions; /* Liste de découpages */ - - portions = g_exe_format_get_portions(format); - - if (portions == NULL) - result = false; - - else - { - result = g_binary_portion_translate_address_into_vmpa(portions, addr, pos); - - g_object_unref(G_OBJECT(portions)); - - } - - return result; + G_OBJECT_CLASS(gtk_python_notebook_tweak_panel_parent_class)->finalize(object); } diff --git a/plugins/pynb/prefs.h b/plugins/pynb/prefs.h new file mode 100644 index 0000000..6551983 --- /dev/null +++ b/plugins/pynb/prefs.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * prefs.h - prototypes pour la configuration des paramètres liés aux notes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_PYNB_PREFS_H +#define _PLUGINS_PYNB_PREFS_H + + +#include <gtk/gtk.h> + + +#include <glibext/helpers.h> + + + +#define GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL (gtk_python_notebook_tweak_panel_get_type()) + +DECLARE_GTYPE(GtkPythonNotebookTweakPanel, gtk_python_notebook_tweak_panel, GTK, PYTHON_NOTEBOOK_TWEAK_PANEL); + + + +#endif /* _PLUGINS_PYNB_PREFS_H */ diff --git a/plugins/pynb/prefs.ui b/plugins/pynb/prefs.ui new file mode 100644 index 0000000..b8a6962 --- /dev/null +++ b/plugins/pynb/prefs.ui @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkPythonNotebookTweakPanel" parent="GtkBox"> + + <property name="orientation">vertical</property> + + <!-- Conservation de paramètres sécurisée --> + <child> + <object class="GtkGrid"> + <property name="margin-start">20</property> + <property name="margin-end">20</property> + <property name="margin-top">20</property> + <property name="margin-bottom">20</property> + <property name="row-spacing">10</property> + <property name="column-spacing">10</property> + + <child> + <object class="GtkLabel"> + <property name="label">Notes config</property> + <property name="use-markup">true</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="column-span">2</property> + </layout> + <style> + <class name="heading"/> + </style> + </object> + </child> + + </object> + </child> + + </template> +</interface> diff --git a/src/Makefile.am b/src/Makefile.am index 1250b59..7411287 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ lib_LTLIBRARIES = libchrysacore4.la libchrysacoreui.la # libchrysacore.la -bin_PROGRAMS = framework # chrysalide chrysalide-hub rost +bin_PROGRAMS = chrysalide # chrysalide-hub rost AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/intl @@ -39,18 +39,18 @@ GUI_SUBDIR = \ endif -libchrysacore_la_LIBADD = \ - analysis/libanalysis.la \ - arch/libarch.la \ - common/libcommon.la \ - core/libcore.la \ - debug/libdebug.la \ - format/libformat.la \ - glibext/libglibext.la \ - $(GTKEXT_LIBADD) \ - $(GUI_LIBADD) \ - mangling/libmangling.la \ - plugins/libplugins.la +# libchrysacore_la_LIBADD = \ +# analysis/libanalysis.la \ +# arch/libarch.la \ +# common/libcommon.la \ +# core/libcore.la \ +# debug/libdebug.la \ +# format/libformat.la \ +# glibext/libglibext.la \ +# $(GTKEXT_LIBADD) \ +# $(GUI_LIBADD) \ +# mangling/libmangling.la \ +# plugins/libplugins.la # -ldl: dladdr(), dlerror() @@ -59,7 +59,7 @@ libchrysacore_la_LDFLAGS = \ -avoid-version -ldl -lm \ $(TOOLKIT_LIBS) $(LIBXML_LIBS) \ $(LIBSQLITE_LIBS) $(LIBARCHIVE_LIBS) \ - $(LIBSSL_LIBS) $(LIBHS_LIBS) + $(LIBSSL_LIBS) if BUILD_CURL_SUPPORT @@ -73,19 +73,32 @@ libchrysacore_la_LDFLAGS += $(LIBMAGIC_LIBS) endif +if BUILD_HS_SUPPORT + +libchrysacore_la_LDFLAGS += $(LIBHS_LIBS) + +endif + libchrysacore4_la_SOURCES = libchrysacore4_la_LIBADD = \ analysis/libanalysis4.la \ - arch/libarch4.la \ + arch/libarch.la \ common/libcommon4.la \ core/libcore4.la \ - glibext/libglibext4.la \ + format/libformat.la \ + glibext/libglibext.la \ plugins/libplugins.la libchrysacore4_la_LDFLAGS = \ - $(TOOLKIT_LIBS) $(LIBSSL_LIBS) + $(TOOLKIT_LIBS) $(LIBSSL_LIBS) $(LIBJSONGLIB_LIBS) $(LIBZIP_LIBS) + +if BUILD_CURL_SUPPORT + +libchrysacore4_la_LDFLAGS += $(LIBCURL_LIBS) + +endif #--- libchrysacoreui @@ -95,13 +108,15 @@ EXTRA_libchrysacoreui_la_DEPENDENCIES = libchrysacore4.la libchrysacoreui_la_SOURCES = libchrysacoreui_la_LIBADD = \ + arch/libarchui.la \ + data/images/libdataimages.la \ glibext/libglibextui.la \ gtkext/libgtkext4.la \ gui/libgui4.la # -lm: log() libchrysacoreui_la_LDFLAGS = \ - $(LIBGTK4_CFLAGS) -L.libs -lchrysacore4 \ + $(LIBGTK4_LIBS) -L.libs -lchrysacore4 \ -lm @@ -111,33 +126,33 @@ libchrysacoreui_la_LDFLAGS = \ # Programme principal ############################################################ -EXTRA_chrysalide_DEPENDENCIES = libchrysacore.la +EXTRA_chrysalide0_DEPENDENCIES = libchrysacore.la -chrysalide_SOURCES = \ +chrysalide0_SOURCES = \ $(GOBJECT_LEAKS_SOURCES) \ main.c -chrysalide_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) +chrysalide0_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) -chrysalide_LDFLAGS = $(TOOLKIT_LIBS) -L/usr/X11R6/lib -ldl -lm $(LIBXML_LIBS) $(LIBPYTHON_LIBS) $(LIBARCHIVE_LIBS) $(LIBSQLITE_LIBS) \ +chrysalide0_LDFLAGS = $(TOOLKIT_LIBS) -L/usr/X11R6/lib -ldl -lm $(LIBXML_LIBS) $(LIBPYTHON_LIBS) $(LIBARCHIVE_LIBS) $(LIBSQLITE_LIBS) \ -L.libs -lchrysacore -chrysalide_LDADD = $(LIBINTL) +chrysalide0_LDADD = $(LIBINTL) -EXTRA_framework_DEPENDENCIES = libchrysacore4.la libchrysacoreui.la +EXTRA_chrysalide_DEPENDENCIES = libchrysacore4.la libchrysacoreui.la -framework_SOURCES = \ - framework.h framework.c +chrysalide_SOURCES = \ + app.h app.c -framework_CFLAGS = $(TOOLKIT4_CFLAGS) +chrysalide_CFLAGS = $(TOOLKIT4_CFLAGS) $(LIBGIOUNIX_CFLAGS) -framework_LDFLAGS = $(TOOLKIT4_LIBS) \ +chrysalide_LDFLAGS = $(TOOLKIT4_LIBS) $(LIBGIOUNIX_LIBS) \ -L.libs -lchrysacore4 -lchrysacoreui -framework_LDADD = +chrysalide_LDADD = @@ -181,4 +196,4 @@ rost_LDFLAGS = $(LIBGOBJ_LIBS) -L.libs -lchrysacore #SUBDIRS = core glibext $(GTKEXT_SUBDIR) analysis arch format common debug $(GUI_SUBDIR) mangling plugins schemas -SUBDIRS = analysis arch common core glibext gtkext gui plugins +SUBDIRS = analysis arch common core data format glibext gtkext gui plugins schemas diff --git a/src/analysis/contents/encapsulated.c b/src/analysis/contents/encapsulated.c index 451c340..22b61c0 100644 --- a/src/analysis/contents/encapsulated.c +++ b/src/analysis/contents/encapsulated.c @@ -30,8 +30,8 @@ #include "encapsulated-int.h" #include "../db/misc/rlestr.h" -#include "../storage/serialize-int.h" #include "../../common/extstr.h" +#include "../../glibext/serialize-int.h" diff --git a/src/analysis/contents/file.c b/src/analysis/contents/file.c index 791f8b6..125016c 100644 --- a/src/analysis/contents/file.c +++ b/src/analysis/contents/file.c @@ -35,8 +35,8 @@ #include "file-int.h" #include "../db/misc/rlestr.h" -#include "../storage/serialize-int.h" #include "../../core/logs.h" +#include "../../glibext/serialize-int.h" diff --git a/src/analysis/contents/memory.c b/src/analysis/contents/memory.c index f7d2bd0..c6ccb5b 100644 --- a/src/analysis/contents/memory.c +++ b/src/analysis/contents/memory.c @@ -34,11 +34,10 @@ #include "memory-int.h" -#include "../content-int.h" #include "../db/misc/rlestr.h" -#include "../storage/serialize-int.h" #include "../../common/extstr.h" #include "../../core/logs.h" +#include "../../glibext/serialize-int.h" @@ -337,6 +336,7 @@ bool g_memory_content_create(GMemoryContent *content, const bin_t *data, phys_t if (allocated == NULL) { LOG_ERROR_N("malloc"); + result = false; goto exit; } diff --git a/src/analysis/contents/restricted.c b/src/analysis/contents/restricted.c index 95f513b..6f091ae 100644 --- a/src/analysis/contents/restricted.c +++ b/src/analysis/contents/restricted.c @@ -30,9 +30,9 @@ #include "restricted-int.h" #include "../db/misc/rlestr.h" -#include "../storage/serialize-int.h" #include "../../common/extstr.h" #include "../../core/logs.h" +#include "../../glibext/serialize-int.h" diff --git a/src/analysis/scan/patterns/backends/Makefile.am b/src/analysis/scan/patterns/backends/Makefile.am index 23b0163..84711a7 100644 --- a/src/analysis/scan/patterns/backends/Makefile.am +++ b/src/analysis/scan/patterns/backends/Makefile.am @@ -6,10 +6,16 @@ libanalysisscanpatternsbackends_la_SOURCES = \ acism-int.h \ acism.h acism.c \ bitap-int.h \ - bitap.h bitap.c \ + bitap.h bitap.c + +if BUILD_HS_SUPPORT + +libanalysisscanpatternsbackends_la_SOURCES += \ hyperscan-int.h \ hyperscan.h hyperscan.c +endif + # Cf. https://www.gnu.org/software/automake/manual/html_node/Per_002dObject-Flags.html AM_CFLAGS = $(LIBGOBJ_CFLAGS) $(LIBHS_CFLAGS) diff --git a/src/analysis/storage/Makefile.am b/src/analysis/storage/Makefile.am index 3eb287b..dad7411 100644 --- a/src/analysis/storage/Makefile.am +++ b/src/analysis/storage/Makefile.am @@ -6,12 +6,7 @@ libanalysisstorage_la_SOURCES = \ cache-int.h \ cache.h cache.c \ container-int.h \ - container.h container.c \ - serialize-int.h \ - serialize.h serialize.c \ - storage-int.h \ - storage.h storage.c \ - tpmem.h tpmem.c + container.h container.c libanalysisstorage_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) diff --git a/src/analysis/storage/serialize.h b/src/analysis/storage/serialize.h deleted file mode 100644 index 93a4496..0000000 --- a/src/analysis/storage/serialize.h +++ /dev/null @@ -1,64 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * serialize.h - prototypes pour les objets entreposables dans un cache - * - * Copyright (C) 2020 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. - */ - - -#ifndef _ANALYSIS_STORAGE_SERIALIZE_H -#define _ANALYSIS_STORAGE_SERIALIZE_H - - -#include <glib-object.h> - - -#include "../../common/packed.h" - - - -#define G_TYPE_SERIALIZABLE_OBJECT g_serializable_object_get_type() -#define G_SERIALIZABLE_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SERIALIZABLE_OBJECT, GSerializableObject)) -#define G_SERIALIZABLE_OBJECT_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_SERIALIZABLE_OBJECT, GSerializableObjectIface)) -#define G_IS_SERIALIZABLE_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SERIALIZABLE_OBJECT)) -#define G_IS_SERIALIZABLE_OBJECT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_SERIALIZABLE_OBJECT)) -#define G_SERIALIZABLE_OBJECT_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_SERIALIZABLE_OBJECT, GSerializableObjectIface)) - - -/* Intermédiaire pour la mise en cache d'objet (coquille vide) */ -typedef struct _GSerializableObject GSerializableObject; - -/* Intermédiaire pour la mise en cache d'objet (interface) */ -typedef struct _GSerializableObjectIface GSerializableObjectIface; - - -/* Détermine le type d'une interface pour la mise en cache d'objet. */ -GType g_serializable_object_get_type(void) G_GNUC_CONST; - -/* storage.h : définition d'une conservation d'objets construits */ -typedef struct _GObjectStorage GObjectStorage; - -/* Charge un objet depuis une mémoire tampon. */ -bool g_serializable_object_load(GSerializableObject *, GObjectStorage *, packed_buffer_t *); - -/* Sauvegarde un objet dans une mémoire tampon. */ -bool g_serializable_object_store(const GSerializableObject *, GObjectStorage *, packed_buffer_t *); - - - -#endif /* _ANALYSIS_STORAGE_SERIALIZE_H */ diff --git a/src/analysis/storage/tpmem.h b/src/analysis/storage/tpmem.h deleted file mode 100644 index 34cbde6..0000000 --- a/src/analysis/storage/tpmem.h +++ /dev/null @@ -1,70 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * tpmem.h - prototypes pour la mémorisation des types d'objets mis en cache - * - * Copyright (C) 2020 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Foobar. If not, see <http://www.gnu.org/licenses/>. - */ - - -#ifndef _ANALYSIS_STORAGE_TPMEM_H -#define _ANALYSIS_STORAGE_TPMEM_H - - -#include <glib-object.h> - - -#include "../../common/packed.h" - - - -#define G_TYPE_TYPE_MEMORY g_type_memory_get_type() -#define G_TYPE_MEMORY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_TYPE_MEMORY, GTypeMemory)) -#define G_IS_TYPE_MEMORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_TYPE_MEMORY)) -#define G_TYPE_MEMORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_TYPE_MEMORY, GTypeMemoryClass)) -#define G_IS_TYPE_MEMORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_TYPE_MEMORY)) -#define G_TYPE_MEMORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_TYPE_MEMORY, GTypeMemoryClass)) - - -/* Définition d'une mémoire de types d'objets (instance) */ -typedef struct _GTypeMemory GTypeMemory; - -/* Définition d'une mémoire de types d'objets (classe) */ -typedef struct _GTypeMemoryClass GTypeMemoryClass; - - -/* Indique le type défini pour une mémoire de types d'objets. */ -GType g_type_memory_get_type(void); - -/* Crée une mémoire pour types d'objets. */ -GTypeMemory *g_type_memory_new(void); - -/* Apprend tous les types mémorisés dans un tampon. */ -bool g_type_memory_load_types(GTypeMemory *, packed_buffer_t *); - -/* Crée une nouvelle instance d'objet à partir de son type. */ -GObject *g_type_memory_create_object(GTypeMemory *, packed_buffer_t *); - -/* Sauvegarde le type d'un objet instancié. */ -bool g_type_memory_store_object_gtype(GTypeMemory *, GObject *, packed_buffer_t *); - -/* Enregistre tous les types mémorisés dans un tampon. */ -bool g_type_memory_store_types(GTypeMemory *, packed_buffer_t *); - - - -#endif /* _ANALYSIS_STORAGE_TPMEM_H */ diff --git a/src/framework.c b/src/app.c index 96c97c3..ef62135 100644 --- a/src/framework.c +++ b/src/app.c @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * framework.c - fichier d'entrée du programme + * app.c - fichier d'entrée du programme * * Copyright (C) 2024 Cyrille Bagard * @@ -22,15 +22,24 @@ */ +#include <assert.h> +#include <fcntl.h> +#include <getopt.h> +#include <libgen.h> +#include <string.h> +#include <gio/gdesktopappinfo.h> +#include <gio/gio.h> +#include <sys/auxv.h> -#include <gtk/gtk.h> - -#include "framework.h" -#include "glibext/helpers.h" +#include "app.h" +#include "common/io.h" +#include "common/xdg.h" +#include "core/core.h" +#include "core/logs.h" #include "gui/core/core.h" -#include "analysis/contents/file.h" // REMME -#include "gtkext/hexview.h" // REMME +#include "gui/window.h" +#include "plugins/pglist.h" @@ -42,8 +51,6 @@ struct _GtkChrysalideFramework { GtkApplication parent; /* A laisser en premier */ - GSettings *settings; /* Paramètres globaux */ - GtkApplicationWindow *main_window; /* Fenêtre principale */ }; @@ -78,6 +85,20 @@ static void gtk_chrysalide_framework_activate(GApplication *); +/* ---------------------- POINT D'ENTREE PRINCIPAL D'EXECUTION ---------------------- */ + + +/* Affiche des indications quant à l'utilisation du programme. */ +static void show_chrysalide_help(const char *); + +/* Affiche des indications sur la version courante du programme. */ +static void show_chrysalide_version(void); + +/* Installe au besoin une définition locale pour le système. */ +static void ensure_wm_icon_and_name(void); + + + /* ---------------------------------------------------------------------------------- */ /* DEFINITION D'APPLICATION PERSONNALISEE */ /* ---------------------------------------------------------------------------------- */ @@ -131,8 +152,6 @@ static void gtk_chrysalide_framework_class_init(GtkChrysalideFrameworkClass *kla static void gtk_chrysalide_framework_init(GtkChrysalideFramework *app) { - app->settings = NULL; - app->main_window = NULL; } @@ -152,8 +171,6 @@ static void gtk_chrysalide_framework_init(GtkChrysalideFramework *app) static void gtk_chrysalide_framework_dispose(GtkChrysalideFramework *app) { - g_clear_object(&app->settings); - g_clear_object(&app->main_window); G_OBJECT_CLASS(gtk_chrysalide_framework_parent_class)->dispose(G_OBJECT(app)); @@ -197,7 +214,7 @@ GtkChrysalideFramework *gtk_chrysalide_framework_new(void) GtkChrysalideFramework *result; /* Instance à retourner */ result = g_object_new(GTK_TYPE_CHRYSALIDE_FRAMEWORK, - "application-id", "re.chrysalide.framework", + "application-id", FRAMEWORK_WINDOW_ID, "flags", G_APPLICATION_DEFAULT_FLAGS, NULL); @@ -227,104 +244,183 @@ GtkChrysalideFramework *gtk_chrysalide_framework_new(void) static void gtk_chrysalide_framework_activate(GApplication *app) { GtkChrysalideFramework *real_app; /* Version réelle de l'instance*/ - GSettings *settings; /* Paramètres globaux */ - GtkWindow *window; /* Fenêtre mise en place */ - GtkCssProvider *css; /* Feuille de style maison */ real_app = GTK_CHRYSALIDE_FRAMEWORK(app); - /* Paramètres globaux */ + real_app->main_window = gtk_framework_window_new(GTK_APPLICATION(app)); + g_object_ref(G_OBJECT(real_app->main_window)); + + gtk_window_present(GTK_WINDOW(real_app->main_window)); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* POINT D'ENTREE PRINCIPAL D'EXECUTION */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : name = nom du programme en question. * +* * +* Description : Affiche des indications quant à l'utilisation du programme. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - settings = g_settings_new("re.chrysalide.framework.mainapp"); - real_app->settings = settings; +static void show_chrysalide_help(const char *name) +{ + char *tmp; /* Conservation modifiable */ + char *base; /* Version courte du nom */ - /* Fenêtre principale */ + tmp = strdup(name); - window = GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(app))); + base = basename(tmp); - real_app->main_window = GTK_APPLICATION_WINDOW(window); - g_object_ref(G_OBJECT(window)); + printf("\n"); - g_settings_bind(settings, "window-width", G_OBJECT(window), "default-width", G_SETTINGS_BIND_DEFAULT); - g_settings_bind(settings, "window-height", G_OBJECT(window), "default-height", G_SETTINGS_BIND_DEFAULT); - g_settings_bind(settings, "window-maximized", G_OBJECT(window), "maximized", G_SETTINGS_BIND_DEFAULT); + printf("Usage: %s [--help] [--version]\n", base); + printf(" %s [args] <filename(s)...>\n", base); - gtk_window_set_title(window, "Chrysalide"); - gtk_window_present(window); + free(tmp); + printf("\n"); - do - { - GBinContent *cnt; + printf("\t-h --help\t\tShow this help message.\n"); + printf("\t-v --version\t\tDisplay the program version.\n"); - GtkWidget *parent; - GtkHexView *wgt; + printf("\n"); + printf("\t-V --verbosity=level\tSet the log level (0 for all messages, %u for none).\n", LMT_COUNT); -#if 0 + printf("\n"); - cnt = g_file_content_new("tmp.bin"); +} - parent = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); - gtk_box_set_homogeneous(GTK_BOX(parent), FALSE); - gtk_window_set_child(window, parent); - wgt = gtk_hex_view_new(cnt); - gtk_box_append(GTK_BOX(parent), GTK_WIDGET(wgt)); +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Affiche des indications sur la version courante du programme.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - wgt = gtk_hex_view_new(cnt); - gtk_box_append(GTK_BOX(parent), GTK_WIDGET(wgt)); +static void show_chrysalide_version(void) +{ + printf("\n"); -#else + printf("-o- Chrysalide r%u -o-\n", REVISION); + printf(_("Compiled on %s at %s\n"), __DATE__, __TIME__); - cnt = g_file_content_new("/bin/id"); - //cnt = g_file_content_new("tmp.bin"); + printf("\n"); +} - parent = gtk_scrolled_window_new(); - gtk_window_set_child(window, parent); - wgt = gtk_hex_view_new(cnt); - gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(parent), GTK_WIDGET(wgt)); +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Installe au besoin une définition locale pour le système. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ -#endif +static void ensure_wm_icon_and_name(void) +{ + GDesktopAppInfo *info; /* Information du système */ + GKeyFile *kfile; /* Définition d'application */ + unsigned long exec_path; /* Chemin du programme */ + char *filename; /* Nom de fichier à écrire */ + GBytes *res; /* Données brutes d'une image */ + gsize size; /* Taille de ces données */ + gconstpointer data; /* Pointeur vers les données */ + int fd; /* Flux ouvert en écriture */ + /* Evaluation du besoin */ + info = g_desktop_app_info_new(FRAMEWORK_WINDOW_ID ".desktop"); + /** + * Si l'exécutable n'est pas valide (inconnu de $PATH), + * la variable info n'est pas initialisée. + */ + if (info != NULL) + { + unref_object(info); + goto done; } - while (0); + /* Mise en place d'une définition d'application */ - /* Chargement des extensions de thème */ + exec_path = getauxval(AT_EXECFN); + assert(exec_path != 0); - /* - css = gtk_css_provider_new(); + kfile = g_key_file_new(); - gtk_css_provider_load_from_resource(css, "/re/chrysalide/framework/glibext/linestyle.css"); + g_key_file_set_string(kfile, "Desktop Entry", "Name", "Chrysalide"); + g_key_file_set_string(kfile, "Desktop Entry", "Comment[fr]", "Cadriciel de rétronception ciblant principalement les systèmes embarqués"); + g_key_file_set_string(kfile, "Desktop Entry", "Comment", "Reverse Engineering Framework focused on embedded systems"); + g_key_file_set_string(kfile, "Desktop Entry", "Type", "Application"); + g_key_file_set_string(kfile, "Desktop Entry", "Exec", (const char *)exec_path); + g_key_file_set_string(kfile, "Desktop Entry", "Icon", "chrysalide-logo"); + g_key_file_set_string(kfile, "Desktop Entry", "StartupNotify", "true"); + g_key_file_set_string(kfile, "Desktop Entry", "MimeType", "application/vnd.android.package-archive"); - gtk_style_context_add_provider_for_display(gtk_widget_get_display(GTK_WIDGET(window)), - GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + filename = get_xdg_data_dir("applications/re.chrysalide.framework.gui.desktop", true); - unref_object(css); - */ + g_key_file_save_to_file(kfile, filename, NULL); - css = gtk_css_provider_new(); + free(filename); - gtk_css_provider_load_from_resource(css, "/re/chrysalide/framework/gtkext/hexview.css"); + g_key_file_free(kfile); - gtk_style_context_add_provider_for_display(gtk_widget_get_display(GTK_WIDGET(window)), - GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + /* Ecriture de l'image */ - unref_object(css); + res = g_resources_lookup_data("/re/chrysalide/framework/images/chrysalide-logo.svg", + G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); + assert(res != NULL); -} + data = g_bytes_get_data(res, &size); + filename = get_xdg_data_dir("icons/hicolor/scalable/apps/chrysalide-logo.svg", true); + fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); -/* ---------------------------------------------------------------------------------- */ -/* POINT D'ENTREE PRINCIPAL D'EXECUTION */ -/* ---------------------------------------------------------------------------------- */ + if (fd == -1) + LOG_ERROR_N("open"); + + else + { + safe_write(fd, data, size); + close(fd); + } + + free(filename); + + g_bytes_unref(res); + + /* Sortie */ + + done: + + ; + +} /****************************************************************************** @@ -343,18 +439,125 @@ static void gtk_chrysalide_framework_activate(GApplication *app) int main(int argc, char **argv) { int result; /* Bilan de l'exécution */ + bool show_help; /* Affichage de l'aide ? */ + bool show_version; /* Affichage de la version ? */ + LogMessageType verbosity; /* Niveau de filtre de message */ + int index; /* Indice d'argument */ + int ret; /* Bilan d'un appel */ GtkChrysalideFramework *app; /* Gestion d'application GTK */ - if (!load_gui_components(AGC_BUFFER_FEATURES)) - return EXIT_FAILURE; + static struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "verbosity", required_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + result = EXIT_FAILURE; + + /* Décodage des options */ + + show_help = false; + show_version = false; + + verbosity = LMT_COUNT; + + while (true) + { + ret = getopt_long(argc, argv, "hvV:", long_options, &index); + if (ret == -1) break; + + switch (ret) + { + case 'h': + show_help = true; + break; + + case 'v': + show_version = true; + break; + + case 'V': + verbosity = strtoul(optarg, NULL, 10); + break; + + } + + } + + /* Actions de base */ + + if (show_help) + { + show_chrysalide_help(argv[0]); + result = EXIT_SUCCESS; + goto exit; + } + + if (show_version) + { + show_chrysalide_version(); + result = EXIT_SUCCESS; + goto exit; + } + + /* Lancement des choses sérieuses */ + + set_log_verbosity(verbosity); + + /** + * Même s'il est dit que l'usage de GtkApplication permet de s'affranchir + * d'un appel à gtk_init(), il se trouve que la journalisation en mode + * graphique peut solliciter le panneau en GTK pour afficher des messages + * avant la mise en place de l'applicatif GTK principal : + * + * #2 0x00007ffff7b33403 in gtk_css_lookup_resolve (lookup=lookup@entry=0x7fffffff88a0, provider=provider@entry=0x0, sstyle=sstyle@entry=0x7fffe800a730, parent_style=parent_style@entry=0x0) at ../gtk/gtkcssstaticstyle.c:911 + * #3 0x00007ffff7b3389f in gtk_css_static_style_new_compute (provider=0x0, filter=filter@entry=0x7fffffff8ea0, node=node@entry=0x0, change=<optimized out>, change@entry=0) at ../gtk/gtkcssstaticstyle.c:1026 + * #4 0x00007ffff7b3392c in gtk_css_static_style_get_default () at ../gtk/gtkcssstaticstyle.c:711 + * #5 0x00007ffff7b24b09 in gtk_css_node_init (cssnode=0x7fffe800a5b0) at ../gtk/gtkcssnode.c:641 + * #6 0x00007ffff75d8dab in g_type_create_instance () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0 + * [...] + * #10 0x00007ffff7b3b8fa in gtk_css_widget_node_new (widget=widget@entry=0x5555555bf1b0) at ../gtk/gtkcsswidgetnode.c:160 + * #11 0x00007ffff7ae4576 in gtk_widget_init (instance=<optimized out>, g_class=0x5555555be380) at ../gtk/gtkwidget.c:2345 + * [...] + * #16 0x00007ffff73fd3f4 in get_framework_panel_singleton (...) at panels.c:329 + * #17 0x00007ffff73fce35 in do_log_message_alt (...) at logs.c:56 + * [...] + * #23 0x00007ffff74566db in init_all_plugins (load=true) at pglist.c:103 + * #24 0x0000555555556d44 in main (argc=2, argv=0x7fffffffb568) at app.c:514 + * + * L'initialisation permettant un retour de gtk_settings_get_default() non + * nul est ainsi forcée au plus tôt avec un appel à gtk_init() manuel. + */ + gtk_init(); + + if (!load_core_components(ACC_GLOBAL_VARS)) + goto exit; + + if (!load_gui_components(AGC_BUFFER_FEATURES | AGC_PANELS)) + goto exit_with_core; + + init_all_plugins(true); + + ensure_wm_icon_and_name(); + + g_set_prgname("Chrysalide"); app = gtk_chrysalide_framework_new(); - result = g_application_run(G_APPLICATION(app), argc, argv); + result = g_application_run(G_APPLICATION(app), 0, NULL); g_object_unref(G_OBJECT(app)); - unload_gui_components(AGC_BUFFER_FEATURES); + exit_all_plugins(); + + unload_gui_components(AGC_BUFFER_FEATURES | AGC_PANELS); + + exit_with_core: + + unload_core_components(ACC_GLOBAL_VARS); + + exit: return result; diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..645fa6b --- /dev/null +++ b/src/app.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * app.h - prototypes pour le fichier d'entrée du programme + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _APP_H +#define _APP_H + + +#include <gtk/gtk.h> + + +#include "glibext/helpers.h" + + + +/* --------------------- DEFINITION D'APPLICATION PERSONNALISEE --------------------- */ + + +#define GTK_TYPE_CHRYSALIDE_FRAMEWORK (gtk_chrysalide_framework_get_type()) + +DECLARE_GTYPE(GtkChrysalideFramework, gtk_chrysalide_framework, GTK, CHRYSALIDE_FRAMEWORK); + + +/* Crée une nouvelle application principale pour Chrysalide. */ +GtkChrysalideFramework *gtk_chrysalide_framework_new(void); + + + +/* ---------------------- POINT D'ENTREE PRINCIPAL D'EXECUTION ---------------------- */ + + +/* Point d'entrée du programme. */ +int main(int, char **); + + + +#endif /* _APP_H */ diff --git a/src/arch/Makefile.am b/src/arch/Makefile.am index 6ee2690..6683854 100644 --- a/src/arch/Makefile.am +++ b/src/arch/Makefile.am @@ -1,43 +1,57 @@ -noinst_LTLIBRARIES = libarch4.la # libarch.la +noinst_LTLIBRARIES = libarch.la libarchui.la + +# libarch_la_SOURCES = \ +# archbase.h archbase.c \ +# context-int.h \ +# context.h context.c \ +# instriter.h instriter.c \ +# link.h link.c \ +# post.h post.c \ +# storage.h storage.c + +# libarch_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) + +# libarch_la_LIBADD = \ +# operands/libarchoperands.la + + +# +# processor-int.h \ +# processor.h processor.c \ +# libarch_la_SOURCES = \ - archbase.h archbase.c \ - context-int.h \ - context.h context.c \ - instriter.h instriter.c \ instruction-int.h \ instruction.h instruction.c \ - link.h link.c \ - operand-int.h operand-int.c \ + operand-int.h \ operand.h operand.c \ - post.h post.c \ - processor-int.h \ - processor.h processor.c \ register-int.h \ register.h register.c \ - storage.h storage.c \ vmpa.h vmpa.c -libarch_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) +libarch_la_CFLAGS = $(LIBGOBJ_CFLAGS) libarch_la_LIBADD = \ instructions/libarchinstructions.la \ operands/libarchoperands.la -libarch4_la_SOURCES = \ - vmpa.h vmpa.c +libarchui_la_SOURCES = \ + instruction-ui-int.h \ + instruction-ui.h instruction-ui.c \ + operand-ui-int.h \ + operand-ui.h operand-ui.c -libarch4_la_CFLAGS = $(TOOLKIT_CFLAGS) +libarchui_la_CFLAGS = $(LIBGTK_CFLAGS) -libarch4_la_LIBADD = +libarchui_la_LIBADD = \ + operands/libarchoperandsui.la devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) -dev_HEADERS = $(libarch_la_SOURCES:%c=) - +dev_HEADERS = $(libarch_la_SOURCES:%c=) $(libarchui_la_SOURCES:%c=) -#SUBDIRS = instructions operands +SUBDIRS = instructions operands diff --git a/src/arch/archbase.h b/src/arch/archbase.h index 59bf18e..b29d2a1 100644 --- a/src/arch/archbase.h +++ b/src/arch/archbase.h @@ -50,6 +50,7 @@ typedef uint64_t vmpa_t; #define VMPA_MAX_SIZE 19 +#if 0 /* Taille des données intégrées */ typedef enum _MemoryDataSize { @@ -68,6 +69,7 @@ typedef enum _MemoryDataSize MDS_64_BITS_SIGNED = 0x85 /* Opérande sur 64 bits signés */ } MemoryDataSize; +#endif #define MDS_RANGE(mds) ((mds & 0xf) - 1) @@ -99,13 +101,13 @@ typedef enum _MemoryDataSize __result; \ }) - +#if 0 #define MDS_4_BITS MDS_4_BITS_UNSIGNED #define MDS_8_BITS MDS_8_BITS_UNSIGNED #define MDS_16_BITS MDS_16_BITS_UNSIGNED #define MDS_32_BITS MDS_32_BITS_UNSIGNED #define MDS_64_BITS MDS_64_BITS_UNSIGNED - +#endif /* Etablit la comparaison entre deux adresses. */ diff --git a/src/arch/instruction-int.h b/src/arch/instruction-int.h index 7dbbe27..d426cea 100644 --- a/src/arch/instruction-int.h +++ b/src/arch/instruction-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * instruction-int.h - prototypes pour la définition générique interne des instructions * - * Copyright (C) 2008-2020 Cyrille Bagard + * Copyright (C) 2008-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,18 +25,29 @@ #define _ARCH_INSTRUCTION_INT_H +#include <stdint.h> + + #include "instruction.h" -#include "../analysis/storage/storage.h" #include "../common/array.h" -#include "../glibext/objhole.h" +#include "../glibext/objhole-int.h" /* Indique l'encodage d'une instruction de façon détaillée. */ -typedef const char * (* get_instruction_encoding_fc) (const GArchInstruction *); +typedef char * (* get_instruction_encoding_fc) (const GArchInstruction *); /* Fournit le nom humain de l'instruction manipulée. */ -typedef const char * (* get_instruction_keyword_fc) (GArchInstruction * ); +typedef char * (* get_instruction_keyword_fc) (const GArchInstruction *); + + + +#if 0 + +#include "../analysis/storage/storage.h" + + + /* Complète un désassemblage accompli pour une instruction. */ typedef void (* call_instruction_hook_fc) (GArchInstruction *, InstrProcessHook, GArchProcessor *, GProcContext *, GExeFormat *); @@ -67,80 +78,45 @@ typedef bool (* store_instruction_fc) (GArchInstruction *, GObjectStorage *, pac -/* Informations glissées dans la structure GObject de GArchOperand */ -typedef struct _instr_extra_data_t -{ - itid_t uid; /* Identifiant unique du type */ - - ArchInstrFlag flags; /* Informations complémentaires*/ - -} instr_extra_data_t; +#endif -/* Informations glissées dans la structure GObject de GArchInstruction */ -typedef union _instr_obj_extra_t -{ - instr_extra_data_t data; /* Données embarquées */ - lockable_obj_extra_t lockable; /* Gestion d'accès aux fanions */ -} instr_obj_extra_t; +/* Conservation d'une adresse et de propriétées */ +typedef unsigned long compact_ins_link_t; /* Définition générique d'une instruction d'architecture (instance) */ struct _GArchInstruction { - GObject parent; /* A laisser en premier */ + GThickObject parent; /* A laisser en premier */ -#if 1 //__SIZEOF_INT__ == __SIZEOF_LONG__ + GBinaryPortion *rel_area; /* Zone de référence */ + rel_mrange_t rel_range; /* Emplacement compressé */ /** - * L'inclusion des informations suivantes dépend de l'architecture. - * - * Si la structure GObject possède un trou, on remplit de préférence - * ce dernier. + * A laisser à la suite de la localisation précédente pour éviter + * les espaces vide dans la structure. */ + uint16_t link_count; /* Quantité de liens établis */ - instr_obj_extra_t extra; /* Externalisation embarquée */ - -#endif - - mrange_t range; /* Emplacement en mémoire */ + compact_ins_link_t *links; /* Liste de ces liens */ flat_array_t *operands; /* Liste des opérandes */ - /** - * Il existe le besoin indéniable d'un verrou pour les accès aux instructions - * liées. Il faut par ailleurs un verrou distinct pour les sources et les - * destinations car une même instruction peut boucler sur elle même et la - * fonction g_arch_instruction_change_link() pose des verrous sur les - * deux extrémités. - * - * La GLib propose les fonctions g_bit_lock() / g_bit_unlock(), légères mais - * sans distinction entre lectures et écritures. Tant pis : la réduction de - * l'empreinte mémoire prime ! - * - * Par contre la documentation indique : - * - * """ - * Attempting to lock on two different bits within the same integer is not supported. - * """ - * - * Donc on doit bien conserver un compteur distinct pour chaque extrémité. - * Cela correspond de toute façon à la définition optimisée des tableaux - * suivante. - */ - - flat_array_t *from; /* Origines des références */ - flat_array_t *to; /* Instructions visées */ - }; /* Définition générique d'une instruction d'architecture (classe) */ struct _GArchInstructionClass { - GObjectClass parent; /* A laisser en premier */ + GThickObjectClass parent; /* A laisser en premier */ get_instruction_encoding_fc get_encoding; /* Obtention de l'encodage */ get_instruction_keyword_fc get_keyword; /* Texte humain équivalent */ + +#if 0 + + //get_instruction_encoding_fc get_encoding; /* Obtention de l'encodage */ + //get_instruction_keyword_fc get_keyword; /* Texte humain équivalent */ call_instruction_hook_fc call_hook; /* Décrochages éventuels */ build_instruction_tooltip_fc build_tooltip; /* Construction d'une bulle*/ get_instruction_desc_fc get_desc; /* Description assez complète */ @@ -155,22 +131,50 @@ struct _GArchInstructionClass //get_instruction_rw_regs_fc get_rw_regs; /* Liste des registres liés */ +#endif + }; +/* Met en place une instruction d'architecture. */ +bool g_arch_instruction_create(GArchInstruction *, itid_t); + + + /** * Accès aux informations éventuellement déportées. */ -#if 1 //__SIZEOF_INT__ == __SIZEOF_LONG__ -# define GET_ARCH_INSTR_EXTRA(ins) (instr_extra_data_t *)&ins->extra +#define ARCH_INSTRUCTION_EXTRA_DATA \ + \ + unsigned int reserved : GOBJECT_RESERVED_EXTRA_BITS; \ + \ + /** \ + * itid_t \ + */ \ + unsigned int tid : 16; \ + \ + /** \ + * ArchOperandFlag \ + */ \ + unsigned int flags : 8; -#else -# define GET_ARCH_INSTR_EXTRA(ins) GET_GOBJECT_EXTRA(G_OBJECT(ins), instr_extra_data_t) +/* Informations glissées dans la structure GObject de GArchOperand */ +typedef struct _instruction_extra_data_t +{ + ARCH_INSTRUCTION_EXTRA_DATA; /* Socle commun */ + +} instruction_extra_data_t; + + +#define GET_ARCH_INSTR_EXTRA(op) \ + GET_GOBJECT_EXTRA(op, instruction_extra_data_t) + +#define SET_ARCH_INSTR_EXTRA(op, data) \ + SET_GOBJECT_EXTRA(op, instruction_extra_data_t, data) -#endif /** diff --git a/src/arch/instruction-ui-int.h b/src/arch/instruction-ui-int.h new file mode 100644 index 0000000..b07f40c --- /dev/null +++ b/src/arch/instruction-ui-int.h @@ -0,0 +1,55 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * instruction-ui-int.h - prototypes pour la définition générique interne des instructions sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_INSTRUCTION_UI_INT_H +#define _ARCH_INSTRUCTION_UI_INT_H + + +#include "instruction-ui.h" + + + +#if 0 + +/* Traduit un opérande en version humainement lisible. */ +typedef void (* print_operand_ui_fc) (const GArchOperandUI *, GBufferLine *); + +/* Construit un petit résumé concis de l'opérande. */ +typedef char * (* build_operand_ui_tooltip_fc) (const GArchOperandUI *, const GLoadedBinary *); + + + +/* Définition générique d'un opérande d'architecture (interface) */ +struct _GArchOperandUIInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + print_operand_ui_fc print; /* Texte humain équivalent */ + build_operand_ui_tooltip_fc build_tooltip; /* Définition de description*/ + +}; + +#endif + + +#endif /* _ARCH_INSTRUCTION_UI_INT_H */ diff --git a/src/arch/instruction-ui.c b/src/arch/instruction-ui.c new file mode 100644 index 0000000..7f923d8 --- /dev/null +++ b/src/arch/instruction-ui.c @@ -0,0 +1,254 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * instruction-ui.c - gestion générique des instructions sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "instruction-ui.h" + + +#include "instruction-int.h" +#include "operand-ui.h" +#include "../analysis/content.h" +#include "../common/cpp.h" +#include "../glibext/generator-int.h" +#include "../glibext/options/asm.h" + + + +/* ------------------------ OFFRE DE CAPACITES DE GENERATION ------------------------ */ + + +/* Renseigne sur les propriétés liées à un générateur. */ +static BufferLineFlags g_arch_instruction_ui_get_flags(const GTokenGenerator *, size_t, size_t); + +/* Etablit dans une ligne de rendu le contenu représenté. */ +static void g_arch_instruction_ui_populate_line(const GTokenGenerator *, size_t, size_t, GBufferLine *, void *); + +#if 0 + +/* Retrouve l'emplacement correspondant à une position donnée. */ +static void g_arch_instruction_ui_compute_cursor(const GArchInstruction *, gint, size_t, size_t, GLineCursor **); + +/* Détermine si le conteneur s'inscrit dans une plage donnée. */ +static int g_arch_instruction_ui_contain_cursor(const GArchInstruction *, size_t, size_t, const GLineCursor *); + +#endif + + + +/* ---------------------------------------------------------------------------------- */ +/* OFFRE DE CAPACITES DE GENERATION */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de génération. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_arch_instruction_ui_token_generator_iface_init(GTokenGeneratorInterface *iface) +{ + /** + * La procédure par défaut de iface->count() ne doit pas être retouchée ! + */ + + iface->get_flags = g_arch_instruction_ui_get_flags; + iface->populate = g_arch_instruction_ui_populate_line; + +#if 0 + iface->compute = (linegen_compute_fc)g_arch_instruction_ui_compute_cursor; + iface->contain = (linegen_contain_fc)g_arch_instruction_ui_contain_cursor; +#endif + +} + + +/****************************************************************************** +* * +* Paramètres : generator = générateur à consulter. * +* index = indice de cette même ligne dans le tampon global.* +* repeat = indice d'utilisations successives du générateur. * +* * +* Description : Renseigne sur les propriétés liées à un générateur. * +* * +* Retour : Propriétés particulières associées. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static BufferLineFlags g_arch_instruction_ui_get_flags(const GTokenGenerator *generator, size_t index, size_t repeat) +{ + BufferLineFlags result; /* Fanions à retourner */ + + result = BLF_HAS_CODE; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : generator = générateur à utiliser pour l'impression. * +* index = indice de cette même ligne dans le tampon global.* +* repeat = indice d'utilisations successives du générateur. * +* line = ligne de rendu à compléter. * +* data = éventuelle donnée complémentaire fournie. * +* * +* Description : Etablit dans une ligne de rendu le contenu représenté. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_arch_instruction_ui_populate_line(const GTokenGenerator *generator, size_t index, size_t repeat, GBufferLine *line, void *data) +{ + GArchInstruction *instr; /* Version spécialisée */ + GBinContent *content; /* Contenu brut d'origine */ + mrange_t range; /* Emplacement couvert */ + char *key; /* Mot clef principal */ + size_t count; /* Nombre d'opérandes en place */ + size_t i; /* Boucle de parcours */ + GArchOperand *op; /* Opérande à manipuler */ + + instr = G_ARCH_INSTRUCTION(generator); + content = G_BIN_CONTENT(data); + + /* Prologue */ + + if (g_arch_instruction_get_range(instr, &range)) + { + g_buffer_line_fill_physical(line, ACO_PHYSICAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&range)); + + g_buffer_line_fill_virtual(line, ACO_VIRTUAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&range)); + + g_buffer_line_fill_content(line, ACO_BINARY, content, &range, VMPA_NO_PHYSICAL); + + } + + /* Instruction proprement dite */ + + key = g_arch_instruction_get_keyword(instr); + + g_buffer_line_append_text(line, ACO_ASSEMBLY_HEAD, TRT_INSTRUCTION, SL(key), NULL, G_OBJECT(instr)); + + free(key); + + /* Liste des opérandes */ + + g_thick_object_lock(G_THICK_OBJECT(instr)); + + count = g_arch_instruction_count_operands(instr); + + if (count > 0) + { + op = g_arch_instruction_get_operand(instr, 0); + g_arch_operand_ui_print(G_ARCH_OPERAND_UI(op), line); + unref_object(op); + + for (i = 1; i < count; i++) + { + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_PUNCT, STCSL(","), NULL, NULL); + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_NONE, STCSL(" "), NULL, NULL); + + op = g_arch_instruction_get_operand(instr, i); + g_arch_operand_ui_print(G_ARCH_OPERAND_UI(op), line); + unref_object(op); + + } + + } + + g_thick_object_unlock(G_THICK_OBJECT(instr)); + +} + + +#if 0 + + +/****************************************************************************** +* * +* Paramètres : instr = générateur à consulter. * +* x = position géographique sur la ligne concernée. * +* index = indice de cette même ligne dans le tampon global. * +* repeat = indice d'utilisations successives du générateur. * +* cursor = emplacement à constituer. [OUT] * +* * +* Description : Retrouve l'emplacement correspondant à une position donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_arch_instruction_ui_compute_cursor(const GArchInstruction *instr, gint x, size_t index, size_t repeat, GLineCursor **cursor) +{ + *cursor = g_binary_cursor_new(); + + g_binary_cursor_update(G_BINARY_CURSOR(*cursor), get_mrange_addr(&instr->range)); + +} + + +/****************************************************************************** +* * +* Paramètres : instr = générateur à consulter. * +* index = indice de cette même ligne dans le tampon global. * +* repeat = indice d'utilisations successives du générateur. * +* cursor = emplacement à analyser. * +* * +* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * +* * +* Retour : Bilan de la détermination, utilisable en comparaisons. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int g_arch_instruction_ui_contain_cursor(const GArchInstruction *instr, size_t index, size_t repeat, const GLineCursor *cursor) +{ + int result; /* Conclusion à retourner */ + vmpa2t addr; /* Autre emplacement à comparer*/ + + assert(G_IS_BINARY_CURSOR(cursor)); + + g_binary_cursor_retrieve(G_BINARY_CURSOR(cursor), &addr); + + result = cmp_mrange_with_vmpa(&instr->range, &addr); + + return result; + +} + + +#endif diff --git a/src/arch/instruction-ui.h b/src/arch/instruction-ui.h new file mode 100644 index 0000000..62a52f2 --- /dev/null +++ b/src/arch/instruction-ui.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * instruction-ui.h - prototypes pour la gestion générique des instructions sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_INSTRUCTION_UI_H +#define _ARCH_INSTRUCTION_UI_H + + +#include "../glibext/generator.h" +#include "../glibext/helpers.h" + + + +/* ------------------------ OFFRE DE CAPACITES DE GENERATION ------------------------ */ + + +/* Procède à l'initialisation de l'interface de génération. */ +void g_arch_instruction_ui_token_generator_iface_init(GTokenGeneratorInterface *); + + + +#endif /* _ARCH_INSTRUCTION_UI_H */ diff --git a/src/arch/instruction.c b/src/arch/instruction.c index cd1e9c7..36bdecb 100644 --- a/src/arch/instruction.c +++ b/src/arch/instruction.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * instruction.c - gestion générique des instructions * - * Copyright (C) 2008-2020 Cyrille Bagard + * Copyright (C) 2008-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,102 +26,93 @@ #include <assert.h> #include <malloc.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> +#include <limits.h> #include <string.h> #include "instruction-int.h" -#include "storage.h" -#include "../analysis/storage/serialize-int.h" -#include "../core/columns.h" +#include "../common/leb128.h" #include "../core/logs.h" #include "../core/processors.h" -#include "../glibext/gbinarycursor.h" -#include "../glibext/linegen-int.h" +#include "../glibext/serialize-int.h" -/* Initialise la classe générique des instructions. */ -static void g_arch_instruction_class_init(GArchInstructionClass *); - -/* Initialise une instance d'opérande d'architecture. */ -static void g_arch_instruction_init(GArchInstruction *); -/* Procède à l'initialisation de l'interface de génération. */ -static void g_arch_instruction_generator_init(GLineGeneratorInterface *); -/* Procède à l'initialisation de l'interface de sérialisation. */ -static void g_arch_instruction_serializable_init(GSerializableObjectInterface *); - -/* Supprime toutes les références externes. */ -static void g_arch_instruction_dispose(GArchInstruction *); - -/* Procède à la libération totale de la mémoire. */ -static void g_arch_instruction_finalize(GArchInstruction *); +/* ----------------------- DEFINITION GENERIQUE D'INSTRUCTION ----------------------- */ +/* Initialise la classe générique des instructions. */ +static void g_arch_instruction_class_init(GArchInstructionClass *); +/* Procède à l'initialisation de l'interface de sérialisation. */ +static void g_arch_instruction_serializable_object_iface_init(GSerializableObjectInterface *); -/* Charge un contenu depuis une mémoire tampon. */ -static bool g_arch_instruction_load_destinations(GArchInstruction *, GObjectStorage *, packed_buffer_t *); +/* Initialise une instance d'opérande d'architecture. */ +static void g_arch_instruction_init(GArchInstruction *); -/* Sauvegarde toutes les destinations d'une instruction. */ -bool g_arch_instruction_store_destinations(GArchInstruction *, GObjectStorage *, packed_buffer_t *); +/* Supprime toutes les références externes. */ +static void g_arch_instruction_dispose(GObject *); +/* Procède à la libération totale de la mémoire. */ +static void g_arch_instruction_finalize(GObject *); +/* ------------------- DEFINITION DES LIAISONS ENTRE INSTRUCTIONS ------------------- */ -/* ------------------------ OFFRE DE CAPACITES DE GENERATION ------------------------ */ +#define COMPACT_INS_LINK_MASK_DIR (1ul << (__WORDSIZE - 1)) +#define COMPACT_INS_LINK_MASK_TYPE 0xf +#define COMPACT_INS_LINK_MASK (COMPACT_INS_LINK_MASK_DIR | COMPACT_INS_LINK_MASK_TYPE) -/* Indique le nombre de ligne prêtes à être générées. */ -static size_t g_arch_instruction_count_lines(const GArchInstruction *); +#define COMPACT_INS_LINK_FROM (0ul << (__WORDSIZE - 1)) +#define COMPACT_INS_LINK_TO (1ul << (__WORDSIZE - 1)) -#ifdef INCLUDE_GTK_SUPPORT +#define COMPACT_INS_LINK_DIR(cl) (cl & COMPACT_INS_LINK_MASK_DIR) +#define COMPACT_INS_LINK_PTR(cl) ((GArchInstruction *)(cl & ~COMPACT_INS_LINK_MASK)) +#define COMPACT_INS_LINK_TYPE(cl) (cl & COMPACT_INS_LINK_MASK_TYPE) -/* Retrouve l'emplacement correspondant à une position donnée. */ -static void g_arch_instruction_compute_cursor(const GArchInstruction *, gint, size_t, size_t, GLineCursor **); +#define MAKE_COMPACT_INS_LINK(d, i, t) \ + (compact_ins_link_t)(d | (unsigned long)i | t) -/* Détermine si le conteneur s'inscrit dans une plage donnée. */ -static int g_arch_instruction_contain_cursor(const GArchInstruction *, size_t, size_t, const GLineCursor *); +/* Détermine si un type de lien existe dans une instruction. */ +static bool _g_arch_instruction_has_link(const GArchInstruction *, compact_ins_link_t, InstructionLinkType); -#endif +/* Détermine si un lien existe entre deux instructions. */ +static bool _g_arch_instruction_has_link_with(const GArchInstruction *, compact_ins_link_t, const GArchInstruction *); -/* Renseigne sur les propriétés liées à un générateur. */ -static BufferLineFlags g_arch_instruction_get_flags2(const GArchInstruction *, size_t, size_t); +/* Fournit la quantité d'instructions pointant vers une autre. */ +static size_t _g_arch_instruction_count_links(const GArchInstruction *, compact_ins_link_t); -/* Ajoute à un tampon GLib le contenu de l'instance spécifiée. */ -static void _g_arch_instruction_print(GArchInstruction *, GBufferLine *, size_t, size_t, const GBinContent *); +/* Fournit les détails d'un lien donné avec une instruction. */ +static GArchInstruction *_g_arch_instruction_get_linked_instruction(const GArchInstruction *, size_t, compact_ins_link_t, InstructionLinkType *); -/* Imprime dans une ligne de rendu le contenu représenté. */ -static void g_arch_instruction_print(GArchInstruction *, GBufferLine *, size_t, size_t, const GBinContent *); -/* -------------------- CONSERVATION ET RECHARGEMENT DES DONNEES -------------------- */ +/* ------------------- MECANISMES DE CONSERVATION ET RESTAURATION ------------------- */ -/* Charge un contenu depuis une mémoire tampon. */ -static bool _g_arch_instruction_load(GArchInstruction *, GObjectStorage *, packed_buffer_t *); +/* Charge un objet depuis un flux de données. */ +static bool g_arch_instruction_load(GSerializableObject *, GObjectStorage *, int); -/* Charge un contenu depuis une mémoire tampon. */ -static bool g_arch_instruction_load(GArchInstruction *, GObjectStorage *, packed_buffer_t *); +/* Sauvegarde un objet dans un flux de données. */ +static bool g_arch_instruction_store(const GSerializableObject *, GObjectStorage *, int); -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool _g_arch_instruction_store(GArchInstruction *, GObjectStorage *, packed_buffer_t *); -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool g_arch_instruction_store(GArchInstruction *, GObjectStorage *, packed_buffer_t *); +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION GENERIQUE D'INSTRUCTION */ +/* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour une instruction d'architecture. */ -G_DEFINE_TYPE_WITH_CODE(GArchInstruction, g_arch_instruction, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_LINE_GENERATOR, g_arch_instruction_generator_init) - G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_arch_instruction_serializable_init)); +G_DEFINE_TYPE_WITH_CODE(GArchInstruction, g_arch_instruction, G_TYPE_THICK_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_arch_instruction_serializable_object_iface_init) + G_IMPLEMENT_INTERFACE_IF_SYM(g_token_generator_get_type, g_arch_instruction_ui_token_generator_iface_init)); + /****************************************************************************** @@ -139,28 +130,20 @@ G_DEFINE_TYPE_WITH_CODE(GArchInstruction, g_arch_instruction, G_TYPE_OBJECT, static void g_arch_instruction_class_init(GArchInstructionClass *klass) { GObjectClass *object; /* Autre version de la classe */ - GArchInstructionClass *instr; /* Encore une autre vision... */ object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_arch_instruction_dispose; - object->finalize = (GObjectFinalizeFunc)g_arch_instruction_finalize; - - instr = G_ARCH_INSTRUCTION_CLASS(klass); - - instr->print = (print_instruction_fc)_g_arch_instruction_print; - - instr->load = (load_instruction_fc)_g_arch_instruction_load; - instr->store = (store_instruction_fc)_g_arch_instruction_store; + object->dispose = g_arch_instruction_dispose; + object->finalize = g_arch_instruction_finalize; } /****************************************************************************** * * -* Paramètres : instr = instance à initialiser. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Initialise une instance d'instruction d'architecture. * +* Description : Procède à l'initialisation de l'interface de sérialisation. * * * * Retour : - * * * @@ -168,27 +151,19 @@ static void g_arch_instruction_class_init(GArchInstructionClass *klass) * * ******************************************************************************/ -static void g_arch_instruction_init(GArchInstruction *instr) +static void g_arch_instruction_serializable_object_iface_init(GSerializableObjectInterface *iface) { - instr_extra_data_t *extra; /* Données insérées à modifier */ - - extra = GET_ARCH_INSTR_EXTRA(instr); - - INIT_GOBJECT_EXTRA_LOCK(extra); - - instr->operands = NULL; - - instr->from = NULL; - instr->to = NULL; + iface->load = g_arch_instruction_load; + iface->store = g_arch_instruction_store; } /****************************************************************************** * * -* Paramètres : iface = interface GLib à initialiser. * +* Paramètres : instr = instance à initialiser. * * * -* Description : Procède à l'initialisation de l'interface de génération. * +* Description : Initialise une instance d'instruction d'architecture. * * * * Retour : - * * * @@ -196,42 +171,22 @@ static void g_arch_instruction_init(GArchInstruction *instr) * * ******************************************************************************/ -static void g_arch_instruction_generator_init(GLineGeneratorInterface *iface) +static void g_arch_instruction_init(GArchInstruction *instr) { - iface->count = (linegen_count_lines_fc)g_arch_instruction_count_lines; -#ifdef INCLUDE_GTK_SUPPORT - iface->compute = (linegen_compute_fc)g_arch_instruction_compute_cursor; - iface->contain = (linegen_contain_fc)g_arch_instruction_contain_cursor; -#endif - iface->get_flags = (linegen_get_flags_fc)g_arch_instruction_get_flags2; - iface->print = (linegen_print_fc)g_arch_instruction_print; - -} + instr->rel_area = NULL; + instr->link_count = 0; -/****************************************************************************** -* * -* Paramètres : iface = interface GLib à initialiser. * -* * -* Description : Procède à l'initialisation de l'interface de sérialisation. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + instr->links = NULL; -static void g_arch_instruction_serializable_init(GSerializableObjectInterface *iface) -{ - iface->load = (load_serializable_object_cb)g_arch_instruction_load; - iface->store = (store_serializable_object_cb)g_arch_instruction_store; + instr->operands = NULL; } /****************************************************************************** * * -* Paramètres : instr = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -241,43 +196,38 @@ static void g_arch_instruction_serializable_init(GSerializableObjectInterface *i * * ******************************************************************************/ -static void g_arch_instruction_dispose(GArchInstruction *instr) +static void g_arch_instruction_dispose(GObject *object) { + GArchInstruction *instr; /* Version spécialisée */ size_t count; /* Nombre d'opérandes en place */ size_t i; /* Boucle de parcours */ GArchOperand *op; /* Opérande à manipuler */ - g_arch_instruction_lock_operands(instr); + instr = G_ARCH_INSTRUCTION(object); + + g_clear_object(&instr->rel_area); + + g_arch_instruction_delete_all_links(instr); - count = _g_arch_instruction_count_operands(instr); + g_thick_object_lock(G_THICK_OBJECT(instr)); + + count = g_arch_instruction_count_operands(instr); for (i = 0; i < count; i++) { - op = _g_arch_instruction_get_operand(instr, 0); + op = g_arch_instruction_get_operand(instr, 0); rem_item_from_flat_array(&instr->operands, 0, sizeof(GArchOperand *)); /** * Une fois pour l'obtention, une autre pour la libération ! */ - g_object_unref(G_OBJECT(op)); - g_object_unref(G_OBJECT(op)); + unref_object(op); + unref_object(op); } - g_arch_instruction_unlock_operands(instr); - -#ifndef NDEBUG - g_arch_instruction_lock_src(instr); - assert(count_flat_array_items(instr->from) == 0); - g_arch_instruction_unlock_src(instr); -#endif - -#ifndef NDEBUG - g_arch_instruction_lock_dest(instr); - assert(count_flat_array_items(instr->to) == 0); - g_arch_instruction_unlock_dest(instr); -#endif + g_thick_object_unlock(G_THICK_OBJECT(instr)); G_OBJECT_CLASS(g_arch_instruction_parent_class)->dispose(G_OBJECT(instr)); @@ -286,7 +236,7 @@ static void g_arch_instruction_dispose(GArchInstruction *instr) /****************************************************************************** * * -* Paramètres : instr = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -296,38 +246,26 @@ static void g_arch_instruction_dispose(GArchInstruction *instr) * * ******************************************************************************/ -static void g_arch_instruction_finalize(GArchInstruction *instr) +static void g_arch_instruction_finalize(GObject *object) { - G_OBJECT_CLASS(g_arch_instruction_parent_class)->finalize(G_OBJECT(instr)); + GArchInstruction *instr; /* Version spécialisée */ -} + instr = G_ARCH_INSTRUCTION(object); + if (instr->links != NULL) + free(instr->links); -/****************************************************************************** -* * -* Paramètres : instr = instruction quelconque à consulter. * -* * -* Description : Indique l'encodage d'une instruction de façon détaillée. * -* * -* Retour : Description humaine de l'encodage utilisé. * -* * -* Remarques : - * -* * -******************************************************************************/ - -const char *g_arch_instruction_get_encoding(const GArchInstruction *instr) -{ - return G_ARCH_INSTRUCTION_GET_CLASS(instr)->get_encoding(instr); + G_OBJECT_CLASS(g_arch_instruction_parent_class)->finalize(G_OBJECT(instr)); } /****************************************************************************** * * -* Paramètres : instr = instruction quelconque à modifier. * -* flag = drapeau d'information complémentaire à planter. * +* Paramètres : instr = instance à initialiser pleinement. * +* tid = identifiant associé au type d'instructions ciblé. * * * -* Description : Ajoute une information complémentaire à une instruction. * +* Description : Met en place une instruction d'architecture. * * * * Retour : Bilan de l'opération. * * * @@ -335,22 +273,18 @@ const char *g_arch_instruction_get_encoding(const GArchInstruction *instr) * * ******************************************************************************/ -bool g_arch_instruction_set_flag(GArchInstruction *instr, ArchInstrFlag flag) +bool g_arch_instruction_create(GArchInstruction *instr, itid_t tid) { bool result; /* Bilan à retourner */ - instr_extra_data_t *extra; /* Données insérées à modifier */ + instruction_extra_data_t extra; /* Données insérées à modifier */ - assert(flag <= AIF_HIGH_USER); + result = true; extra = GET_ARCH_INSTR_EXTRA(instr); - LOCK_GOBJECT_EXTRA(extra); - - result = !(extra->flags & flag); + extra.tid = tid; - extra->flags |= flag; - - UNLOCK_GOBJECT_EXTRA(extra); + SET_ARCH_INSTR_EXTRA(instr, &extra); return result; @@ -359,33 +293,24 @@ bool g_arch_instruction_set_flag(GArchInstruction *instr, ArchInstrFlag flag) /****************************************************************************** * * -* Paramètres : instr = instruction quelconque à modifier. * -* flag = drapeau d'information complémentaire à planter. * +* Paramètres : instr = instruction quelconque à consulter. * * * -* Description : Retire une information complémentaire à une instruction. * +* Description : Fournit l'identifiant correspondant à un type d'instructions.* * * -* Retour : Bilan de l'opération. * +* Retour : Identifiant unique par type d'instruction et architecture. * * * * Remarques : - * * * ******************************************************************************/ -bool g_arch_instruction_unset_flag(GArchInstruction *instr, ArchInstrFlag flag) +itid_t g_arch_instruction_get_type_id(const GArchInstruction *instr) { - bool result; /* Bilan à retourner */ - instr_extra_data_t *extra; /* Données insérées à modifier */ - - assert(flag <= AIF_HIGH_USER); + itid_t result; /* Numéro à retourner */ + instruction_extra_data_t extra; /* Données insérées à modifier */ extra = GET_ARCH_INSTR_EXTRA(instr); - LOCK_GOBJECT_EXTRA(extra); - - result = (extra->flags & flag); - - extra->flags &= ~flag; - - UNLOCK_GOBJECT_EXTRA(extra); + result = extra.tid; return result; @@ -395,30 +320,23 @@ bool g_arch_instruction_unset_flag(GArchInstruction *instr, ArchInstrFlag flag) /****************************************************************************** * * * Paramètres : instr = instruction quelconque à consulter. * -* flag = drapeau d'information à rechercher. * * * -* Description : Détermine si une instruction possède un fanion particulier. * +* Description : Indique l'encodage d'une instruction de façon détaillée. * * * -* Retour : Bilan de la détection. * +* Retour : Description humaine de l'encodage utilisé. * * * * Remarques : - * * * ******************************************************************************/ -bool g_arch_instruction_has_flag(const GArchInstruction *instr, ArchInstrFlag flag) +char *g_arch_instruction_get_encoding(const GArchInstruction *instr) { - bool result; /* Bilan à retourner */ - instr_extra_data_t *extra; /* Données insérées à modifier */ - - assert(flag <= AIF_HIGH_USER); - - extra = GET_ARCH_INSTR_EXTRA(instr); - - LOCK_GOBJECT_EXTRA(extra); + char *result; /* Encodage à retourner */ + GArchInstructionClass *class; /* Classe des instructions */ - result = (extra->flags & flag); + class = G_ARCH_INSTRUCTION_GET_CLASS(instr); - UNLOCK_GOBJECT_EXTRA(extra); + result = class->get_encoding(instr); return result; @@ -427,28 +345,24 @@ bool g_arch_instruction_has_flag(const GArchInstruction *instr, ArchInstrFlag fl /****************************************************************************** * * -* Paramètres : instr = instruction quelconque à modifier. * +* Paramètres : instr = instruction d'assemblage à consulter. * * * -* Description : Fournit les informations complémentaires d'une instruction. * +* Description : Fournit le nom humain de l'instruction manipulée. * * * -* Retour : Eventuels drapeaux d'information complémentaire à plantés. * +* Retour : Mot clef de bas niveau. * * * * Remarques : - * * * ******************************************************************************/ -ArchInstrFlag g_arch_instruction_get_flags(const GArchInstruction *instr) +char *g_arch_instruction_get_keyword(const GArchInstruction *instr) { - ArchInstrFlag result; /* Fanions à retourner */ - instr_extra_data_t *extra; /* Données insérées à modifier */ - - extra = GET_ARCH_INSTR_EXTRA(instr); - - LOCK_GOBJECT_EXTRA(extra); + char *result; /* Etiquette à retourner */ + GArchInstructionClass *class; /* Classe des instructions */ - result = extra->flags; + class = G_ARCH_INSTRUCTION_GET_CLASS(instr); - UNLOCK_GOBJECT_EXTRA(extra); + result = class->get_keyword(instr); return result; @@ -457,10 +371,12 @@ ArchInstrFlag g_arch_instruction_get_flags(const GArchInstruction *instr) /****************************************************************************** * * -* Paramètres : instr = instruction quelconque à consulter. * -* uid = identifiant unique par type d'instruction. * +* Paramètres : instr = instruction quelconque à compléter. * +* area = portion de binaire incluant l'instruction. * +* start = adresse virtuelle et/ou position physique. * +* length = taille de l'instruction. * * * -* Description : Définit l'identifiant unique pour un ensemble d'instructions.* +* Description : Calcule la localisation d'une instruction. * * * * Retour : - * * * @@ -468,358 +384,214 @@ ArchInstrFlag g_arch_instruction_get_flags(const GArchInstruction *instr) * * ******************************************************************************/ -void g_arch_instruction_set_unique_id(GArchInstruction *instr, itid_t uid) +void g_arch_instruction_compute_range(GArchInstruction *instr, GBinaryPortion *area, const vmpa2t *start, phys_t length) { - instr_extra_data_t *extra; /* Données insérées à modifier */ + const mrange_t *a_range; /* Couverture de la portion */ + phys_t diff; /* Décalage à appliquer */ - extra = GET_ARCH_INSTR_EXTRA(instr); - - LOCK_GOBJECT_EXTRA(extra); + a_range = g_binary_portion_get_range(area); - extra->uid = uid; + assert(mrange_contains_addr(a_range, start)); - UNLOCK_GOBJECT_EXTRA(extra); + diff = compute_vmpa_diff(get_mrange_addr(a_range), start); -} + instr->rel_area = area; + ref_object(area); - -/****************************************************************************** -* * -* Paramètres : instr = instruction quelconque à consulter. * -* * -* Description : Fournit l'identifiant unique pour un ensemble d'instructions.* -* * -* Retour : Identifiant unique par type d'instruction et architecture. * -* * -* Remarques : - * -* * -******************************************************************************/ - -itid_t g_arch_instruction_get_unique_id(const GArchInstruction *instr) -{ - itid_t result; /* Numéro à retourner */ - instr_extra_data_t *extra; /* Données insérées à consulter*/ - - extra = GET_ARCH_INSTR_EXTRA(instr); - - LOCK_GOBJECT_EXTRA(extra); - - result = extra->uid; - - UNLOCK_GOBJECT_EXTRA(extra); - - return result; + init_rel_mrange(&instr->rel_range, diff, length); } /****************************************************************************** * * -* Paramètres : instr = instruction quelconque à traiter. * -* type = type de procédure à utiliser. * -* proc = représentation de l'architecture utilisée. * -* context = contexte associé à la phase de désassemblage. * -* format = accès aux données du binaire d'origine. * +* Paramètres : instr = instruction quelconque à consulter. * +* range = localisation de l'instruction. [OUT] * * * -* Description : Complète un désassemblage accompli pour une instruction. * +* Description : Fournit la place mémoire d'une instruction. * * * -* Retour : - * +* Retour : Validité de la localisation : existence d'une définition ? * * * * Remarques : - * * * ******************************************************************************/ -void g_arch_instruction_call_hook(GArchInstruction *instr, InstrProcessHook type, GArchProcessor *proc, GProcContext *context, GExeFormat *format) +bool g_arch_instruction_get_range(const GArchInstruction *instr, mrange_t *range) { - GArchInstructionClass *class; /* Classe des instructions */ + bool result; /* Statut à retourner */ + const mrange_t *a_range; /* Couverture de la portion */ + vmpa2t start; /* Position de départ complète */ - class = G_ARCH_INSTRUCTION_GET_CLASS(instr); + result = (instr->rel_area != NULL); - if (class->call_hook != NULL) - class->call_hook(instr, type, proc, context, format); + if (result) + { + a_range = g_binary_portion_get_range(instr->rel_area); -} + copy_vmpa(&start, get_mrange_addr(a_range)); + advance_vmpa(&start, get_rel_mrange_offset(&instr->rel_range)); + init_mrange(range, &start, get_rel_mrange_length(&instr->rel_range)); -/****************************************************************************** -* * -* Paramètres : instr = instruction quelconque à modifier. * -* address = adresse virtuelle et/ou position physique. * -* length = taille de l'instruction. * -* * -* Description : Définit la localisation d'une instruction. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + } -void g_arch_instruction_set_range(GArchInstruction *instr, const mrange_t *range) -{ - copy_mrange(&instr->range, range); + return result; } /****************************************************************************** * * -* Paramètres : instr = instruction quelconque à consulter. * +* Paramètres : instr = instruction à venir modifier. * +* flag = drapeau d'information complémentaire à planter. * * * -* Description : Fournit la place mémoire d'une instruction. * +* Description : Ajoute une information complémentaire à une instruction. * * * -* Retour : Zone mémoire couverte par l'instruction. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -const mrange_t *g_arch_instruction_get_range(const GArchInstruction *instr) +bool g_arch_instruction_set_flag(GArchInstruction *instr, ArchInstructionFlag flag) { - return &instr->range; + bool result; /* Bilan à retourner */ + instruction_extra_data_t extra; /* Données insérées à modifier */ -} + assert(flag <= AIF_HIGH_USER); + extra = GET_ARCH_INSTR_EXTRA(instr); + result = !(extra.flags & flag); -/****************************************************************************** -* * -* Paramètres : instr = instruction quelconque à consulter. * -* offset = position physique dans le code binaire/NULL. [OUT] * -* length = taille de l'instruction ou NULL. [OUT] * -* address = adresse virtuelle ou position physique/NULL. [OUT] * -* * -* Description : Fournit la localisation d'une instruction. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + extra.flags |= flag; -void g_arch_instruction_get_location(const GArchInstruction *instr, off_t *offset, off_t *length, vmpa_t *address) -{ - //if (offset != NULL) *offset = instr->offset; - //if (length != NULL) *length = instr->length; + SET_ARCH_INSTR_EXTRA(instr, &extra); - //if (address != NULL) *address = instr->address; + return result; } - /****************************************************************************** * * -* Paramètres : instr = instruction à consulter. * -* rregs = liste des rgistres lus. [OUT] * -* rcount = nombre de registres lus. [OUT] * -* wregs = liste des rgistres écrits. [OUT] * -* wcount = nombre de registres écrits. [OUT] * +* Paramètres : instr = instruction à venir modifier. * +* flag = drapeau d'information complémentaire à planter. * * * -* Description : Liste les registres lus et écrits par l'instruction. * +* Description : Retire une information complémentaire à une instruction. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * -* Remarques : Les compteurs de références sont à décrémenter après usage ! * +* Remarques : - * * * ******************************************************************************/ -void g_arch_instruction_get_rw_registers(const GArchInstruction *instr, GArchRegister ***rregs, size_t *rcount, GArchRegister ***wregs, size_t *wcount) +bool g_arch_instruction_unset_flag(GArchInstruction *instr, ArchInstructionFlag flag) { -#if 0 + bool result; /* Bilan à retourner */ + instruction_extra_data_t extra; /* Données insérées à modifier */ - size_t i; /* Boucle de parcours */ + assert(flag <= AIF_HIGH_USER); - *rregs = NULL; - *rcount = 0; - *wregs = NULL; - *wcount = 0; + extra = GET_ARCH_INSTR_EXTRA(instr); - instr->get_rw_regs(instr, rregs, rcount, wregs, wcount); + result = (extra.flags & flag); - for (i = 0; i < *rcount; i++) - g_object_ref(G_OBJECT((*rregs)[i])); + extra.flags &= ~flag; - for (i = 0; i < *wcount; i++) - g_object_ref(G_OBJECT((*wregs)[i])); + SET_ARCH_INSTR_EXTRA(instr, &extra); -#endif + return result; } - -/* ---------------------------------------------------------------------------------- */ -/* MANIPULATION DES OPERANDES */ -/* ---------------------------------------------------------------------------------- */ - - /****************************************************************************** * * -* Paramètres : instr = instruction à mettre à jour. * +* Paramètres : instr = instruction à venir consulter. * +* flag = drapeau d'information à rechercher. * * * -* Description : Verrouille les accès à la liste des opérandes. * +* Description : Détermine si une instruction possède un fanion particulier. * * * -* Retour : - * +* Retour : Bilan de la détection. * * * * Remarques : - * * * ******************************************************************************/ -void g_arch_instruction_lock_operands(GArchInstruction *instr) +bool g_arch_instruction_has_flag(const GArchInstruction *instr, ArchInstructionFlag flag) { - lock_flat_array(&instr->operands); + bool result; /* Bilan à retourner */ + instruction_extra_data_t extra; /* Données insérées à modifier */ -} + assert(flag <= AIF_HIGH_USER); + extra = GET_ARCH_INSTR_EXTRA(instr); -/****************************************************************************** -* * -* Paramètres : instr = instruction à mettre à jour. * -* * -* Description : Déverrouille les accès à la liste des opérandes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + result = (extra.flags & flag); -void g_arch_instruction_unlock_operands(GArchInstruction *instr) -{ - unlock_flat_array(&instr->operands); + return result; } /****************************************************************************** * * -* Paramètres : instr = instance à mettre à jour. * -* operand = instruction à venir associer. * +* Paramètres : instr = instruction à venir consulter. * * * -* Description : Attache un opérande supplémentaire à une instruction. * +* Description : Fournit les particularités de l'instruction. * * * -* Retour : - * +* Retour : Somme de tous les fanions associés à l'opérande. * * * * Remarques : - * * * ******************************************************************************/ -void g_arch_instruction_attach_extra_operand(GArchInstruction *instr, GArchOperand *operand) +ArchInstructionFlag g_arch_instruction_get_flags(const GArchInstruction *instr) { - GSingletonFactory *factory; /* Unise à instances uniques */ - GArchOperand *singleton; /* Instance retenue */ - - factory = get_operands_factory(); - - singleton = G_ARCH_OPERAND(g_singleton_factory_get_instance(factory, G_SINGLETON_CANDIDATE(operand))); - - g_object_unref(G_OBJECT(operand)); - g_object_unref(G_OBJECT(factory)); - - g_arch_instruction_lock_operands(instr); - - add_item_to_flat_array(&instr->operands, &singleton, sizeof(GArchOperand *)); - - g_arch_instruction_unlock_operands(instr); + ArchInstructionFlag result; /* Fanions à retourner */ + instruction_extra_data_t extra; /* Données insérées à modifier */ -} - - -/****************************************************************************** -* * -* Paramètres : instr = instance à consulter. * -* * -* Description : Indique la quantité d'opérandes présents dans l'instruction. * -* * -* Retour : Nombre d'opérandes attachés. * -* * -* Remarques : - * -* * -******************************************************************************/ + extra = GET_ARCH_INSTR_EXTRA(instr); -size_t _g_arch_instruction_count_operands(const GArchInstruction *instr) -{ - size_t result; /* Décompte à retourner */ - - result = count_flat_array_items(instr->operands); + result = extra.flags; return result; } -/****************************************************************************** -* * -* Paramètres : instr = instance à consulter. * -* index = indice de l'opérande concerné. * -* * -* Description : Fournit un opérande donné d'une instruction. * -* * -* Retour : Opérande trouvée. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GArchOperand *_g_arch_instruction_get_operand(const GArchInstruction *instr, size_t index) -{ - GArchOperand *result; /* Opérande à retourner */ - GArchOperand **ptr; /* Adresse dans le tableau */ - - ptr = get_flat_array_item(instr->operands, index, sizeof(GArchOperand *)); - - result = *ptr; - - g_object_ref(G_OBJECT(result)); - - return result; -} +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION DES LIAISONS ENTRE INSTRUCTIONS */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : instr = instance à mettre à jour. * -* old = ancienne opérande à détacher. * -* new = nouvelle opérande à attacher. * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* dir = direction du lien recherché. * +* type = type de lien à détecter. * * * -* Description : Remplace un opérande d'une instruction par un autre. * +* Description : Détermine si un type de lien existe dans une instruction. * * * -* Retour : Bilan de l'opération. * +* Retour : Bilan du statut courant de l'instruction. * * * * Remarques : - * * * ******************************************************************************/ -bool _g_arch_instruction_replace_operand(GArchInstruction *instr, GArchOperand *old, GArchOperand *new) +static bool _g_arch_instruction_has_link(const GArchInstruction *instr, compact_ins_link_t dir, InstructionLinkType type) { bool result; /* Bilan à retourner */ - size_t count; /* Nombre d'opérandes en place */ - size_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à manipuler */ - - result = false; - - count = _g_arch_instruction_count_operands(instr); - - for (i = 0; i < count && !result; i++) - { - op = _g_arch_instruction_get_operand(instr, i); + uint16_t i; /* Boucle de parcours */ - result = (op == old); - - g_object_unref(G_OBJECT(op)); - - } - - if (result) - { - rpl_item_in_flat_array(instr->operands, i - 1, &new, sizeof(GArchOperand *)); + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - g_object_unref(G_OBJECT(old)); + result = false; - } + for (i = 0; i < instr->link_count && !result; i++) + result = COMPACT_INS_LINK_DIR(instr->links[i]) == dir; return result; @@ -828,45 +600,22 @@ bool _g_arch_instruction_replace_operand(GArchInstruction *instr, GArchOperand * /****************************************************************************** * * -* Paramètres : instr = instance à mettre à jour. * -* target = instruction à venir dissocier. * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* type = type de lien à détecter. * * * -* Description : Détache un opérande liée d'une instruction. * +* Description : Détermine si un type de lien amène à une instruction. * * * -* Retour : Bilan de l'opération. * +* Retour : Bilan du statut courant de l'instruction. * * * * Remarques : - * * * ******************************************************************************/ -bool _g_arch_instruction_detach_operand(GArchInstruction *instr, GArchOperand *target) +bool g_arch_instruction_has_src_link(const GArchInstruction *instr, InstructionLinkType type) { bool result; /* Bilan à retourner */ - size_t count; /* Nombre d'opérandes en place */ - size_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à manipuler */ - - result = false; - - count = _g_arch_instruction_count_operands(instr); - for (i = 0; i < count && !result; i++) - { - op = _g_arch_instruction_get_operand(instr, i); - - result = (op == target); - - g_object_unref(G_OBJECT(op)); - - } - - if (result) - { - rem_item_from_flat_array(&instr->operands, i - 1, sizeof(GArchOperand *)); - - g_object_unref(G_OBJECT(target)); - - } + result = _g_arch_instruction_has_link(instr, COMPACT_INS_LINK_FROM, type); return result; @@ -875,78 +624,22 @@ bool _g_arch_instruction_detach_operand(GArchInstruction *instr, GArchOperand *t /****************************************************************************** * * -* Paramètres : instr = instance à consulter. * -* target = instruction à venir retrouver. * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* type = type de lien à détecter. * * * -* Description : Détermine le chemin conduisant à un opérande. * +* Description : Détermine si un type de lien émerge d'une instruction. * * * -* Retour : Chemin d'accès à l'opérande ou NULL en cas d'absence. * +* Retour : Bilan du statut courant de l'instruction. * * * * Remarques : - * * * ******************************************************************************/ -char *g_arch_instruction_find_operand_path(GArchInstruction *instr, const GArchOperand *target) +bool g_arch_instruction_has_dest_link(const GArchInstruction *instr, InstructionLinkType type) { - char *result; /* Chemin à retourner */ - size_t count; /* Nombre d'opérandes en place */ - size_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à manipuler */ - int ret; /* Bilan d'une construction */ - char *sub_path; /* Sous-chemin emprunté */ - - result = NULL; - - g_arch_instruction_lock_operands(instr); - - count = _g_arch_instruction_count_operands(instr); - - /* Première passe : accès direct */ - - for (i = 0; i < count && result == NULL; i++) - { - op = _g_arch_instruction_get_operand(instr, i); - - if (op == target) - { - ret = asprintf(&result, "%zu", i); - if (ret == -1) - { - LOG_ERROR_N("asprintf"); - result = NULL; - } - } - - g_object_unref(G_OBJECT(op)); - - } - - /* Seconde passe : accès profond */ - - for (i = 0; i < count && result == NULL; i++) - { - op = _g_arch_instruction_get_operand(instr, i); - - sub_path = g_arch_operand_find_inner_operand_path(op, target); - - if (sub_path != NULL) - { - ret = asprintf(&result, "%zu:%s", i, sub_path); - if (ret == -1) - { - LOG_ERROR_N("asprintf"); - result = NULL; - } - - free(sub_path); - - } - - g_object_unref(G_OBJECT(op)); - - } + bool result; /* Bilan à retourner */ - g_arch_instruction_unlock_operands(instr); + result = _g_arch_instruction_has_link(instr, COMPACT_INS_LINK_TO, type); return result; @@ -955,104 +648,65 @@ char *g_arch_instruction_find_operand_path(GArchInstruction *instr, const GArchO /****************************************************************************** * * -* Paramètres : instr = instance à consulter. * -* path = chemin d'accès à un opérande à retrouver. * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* dir = direction du lien recherché. * +* linked = seconde instruction à considérer. * * * -* Description : Obtient l'opérande correspondant à un chemin donné. * +* Description : Détermine si un lien existe entre deux instructions. * * * -* Retour : Opérande trouvé ou NULL en cas d'échec. * +* Retour : Bilan du statut courant de l'instruction. * * * * Remarques : - * * * ******************************************************************************/ -GArchOperand *g_arch_instruction_get_operand_from_path(GArchInstruction *instr, const char *path) +static bool _g_arch_instruction_has_link_with(const GArchInstruction *instr, compact_ins_link_t dir, const GArchInstruction *linked) { - GArchOperand *result; /* Opérande trouvée à renvoyer */ - size_t index; /* Indice de l'opérande visé */ - char *end; /* Poursuite du parcours ? */ - GArchOperand *found; /* Opérande trouvé */ - - result = NULL; - - g_arch_instruction_lock_operands(instr); - - /* Recherche au premier niveau */ - - index = strtoul(path, &end, 10); - - if ((index == ULONG_MAX && errno == ERANGE) || (index == 0 && errno == EINVAL)) - { - LOG_ERROR_N("strtoul"); - goto done; - } - - found = _g_arch_instruction_get_operand(instr, index); - if (found == NULL) goto done; - - if (*end == '\0') - { - result = found; - goto done; - } - - /* Recherche en profondeur */ - - assert(*end == ':'); - - result = g_arch_operand_get_inner_operand_from_path(found, end + 1); + bool result; /* Bilan à retourner */ + uint16_t i; /* Boucle de parcours */ - g_object_unref(G_OBJECT(found)); + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - done: + result = false; - g_arch_instruction_unlock_operands(instr); + for (i = 0; i < instr->link_count && !result; i++) + result = COMPACT_INS_LINK_PTR(instr->links[i]) == linked; return result; } - -/* ---------------------------------------------------------------------------------- */ -/* DEFINITION DES LIAISONS ENTRE INSTRUCTIONS */ -/* ---------------------------------------------------------------------------------- */ - - /****************************************************************************** * * -* Paramètres : instr = instruction à mettre à jour. * -* src = sélection de l'extrémité à traiter. * -* lock = indique le sens du verrouillage à mener. * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* src = seconde instruction à considérer. * * * -* Description : Met à disposition un encadrement des accès aux liens. * +* Description : Détermine si une instruction est source d'une autre. * * * -* Retour : - * +* Retour : Bilan du statut courant de l'instruction. * * * * Remarques : - * * * ******************************************************************************/ -void g_arch_instruction_lock_unlock_links(GArchInstruction *instr, bool src, bool lock) +bool g_arch_instruction_has_src_link_with(const GArchInstruction *instr, const GArchInstruction *src) { - flat_array_t **array; /* Choix du tableau ciblé */ + bool result; /* Bilan à retourner */ - array = (src ? &instr->from : &instr->to); + result = _g_arch_instruction_has_link_with(instr, COMPACT_INS_LINK_FROM, src); - if (lock) - lock_flat_array(array); - else - unlock_flat_array(array); + return result; } /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * -* type = type de lien à détecter. * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* dest = seconde instruction à considérer. * * * -* Description : Détermine si un type de lien existe dans une instruction. * +* Description : Détermine si une instruction est destination d'une autre. * * * * Retour : Bilan du statut courant de l'instruction. * * * @@ -1060,30 +714,11 @@ void g_arch_instruction_lock_unlock_links(GArchInstruction *instr, bool src, boo * * ******************************************************************************/ -bool g_arch_instruction_has_link(GArchInstruction *instr, InstructionLinkType type) +bool g_arch_instruction_has_dest_link_with(const GArchInstruction *instr, const GArchInstruction *dest) { bool result; /* Bilan à retourner */ - size_t count; /* Nombre de liens à parcourir */ - size_t i; /* Boucle de parcours */ - const instr_link_t *dlink; /* Définition de destination */ - - result = false; - - g_arch_instruction_lock_dest(instr); - count = g_arch_instruction_count_destinations(instr); - - for (i = 0; i < count && !result; i++) - { - dlink = g_arch_instruction_get_destination(instr, i); - - result = (dlink->type == type); - - unref_instr_link(dlink); - - } - - g_arch_instruction_unlock_dest(instr); + result = _g_arch_instruction_has_link_with(instr, COMPACT_INS_LINK_TO, dest); return result; @@ -1092,54 +727,53 @@ bool g_arch_instruction_has_link(GArchInstruction *instr, InstructionLinkType ty /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * +* Paramètres : instr = instruction dont les informations sont à manipuler. * * dest = ligne visée par la liaison (côté destination). * +* type = type de lien à construire. * * * -* Description : Détermine si un lien est déjà établi entre deux instructions.* +* Description : Etablit un lien entre deux instructions. * * * -* Retour : Bilan de l'état actuel des liaisons. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -bool g_arch_instruction_has_link_to(GArchInstruction *instr, const GArchInstruction *dest) +void g_arch_instruction_link(GArchInstruction *instr, GArchInstruction *dest, InstructionLinkType type) { - bool result; /* Bilan à retourner */ - size_t count; /* Nombre de liens à parcourir */ - size_t i; /* Boucle de parcours */ - const instr_link_t *dlink; /* Définition de destination */ + compact_ins_link_t new_from; /* Nouvel enregistrement #1 */ + compact_ins_link_t new_to; /* Nouvel enregistrement #2 */ - result = false; + new_from = MAKE_COMPACT_INS_LINK(COMPACT_INS_LINK_FROM, instr, type); + ref_object(instr); - g_arch_instruction_lock_dest(instr); + new_to = MAKE_COMPACT_INS_LINK(COMPACT_INS_LINK_TO, dest, type); + ref_object(dest); - count = g_arch_instruction_count_destinations(instr); + g_thick_object_lock(G_THICK_OBJECT(instr)); + g_thick_object_lock(G_THICK_OBJECT(dest)); - for (i = 0; i < count && !result; i++) - { - dlink = g_arch_instruction_get_destination(instr, i); - - result = (dlink->linked == dest); + dest->links = realloc(dest->links, ++dest->link_count * sizeof(compact_ins_link_t)); - unref_instr_link(dlink); + dest->links[dest->link_count - 1] = new_from; - } + instr->links = realloc(instr->links, ++instr->link_count * sizeof(compact_ins_link_t)); - g_arch_instruction_unlock_dest(instr); + instr->links[instr->link_count - 1] = new_to; - return result; + g_thick_object_unlock(G_THICK_OBJECT(dest)); + g_thick_object_unlock(G_THICK_OBJECT(instr)); } /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * +* Paramètres : instr = instruction dont les informations sont à manipuler. * * dest = ligne visée par la liaison (côté destination). * * type = type de lien à construire. * * * -* Description : Etablit un lien entre deux instructions. * +* Description : Supprime un lien entre deux instructions. * * * * Retour : - * * * @@ -1147,43 +781,59 @@ bool g_arch_instruction_has_link_to(GArchInstruction *instr, const GArchInstruct * * ******************************************************************************/ -void g_arch_instruction_link_with(GArchInstruction *instr, GArchInstruction *dest, InstructionLinkType type) +void g_arch_instruction_unlink(GArchInstruction *instr, GArchInstruction *dest, InstructionLinkType type) { - instr_link_t new_src; /* Nouveau lien à définir #1 */ - instr_link_t new_dst; /* Nouveau lien à définir #2 */ + compact_ins_link_t old_from; /* Ancien enregistrement #1 */ + compact_ins_link_t old_to; /* Ancien enregistrement #2 */ + uint16_t i_from; /* Boucle de parcours #1 */ + uint16_t i_to; /* Boucle de parcours #2 */ + bool status; /* Bilan des recherches */ + + old_from = MAKE_COMPACT_INS_LINK(COMPACT_INS_LINK_FROM, instr, type); + + old_to = MAKE_COMPACT_INS_LINK(COMPACT_INS_LINK_TO, dest, type); - /* Côté destination */ + g_thick_object_lock(G_THICK_OBJECT(instr)); + g_thick_object_lock(G_THICK_OBJECT(dest)); - new_src.linked = instr; - new_src.type = type; + for (i_from = 0; i_from < dest->link_count; i_from++) + if (dest->links[i_from] == old_from) + break; - ref_instr_link((&new_src)); + for (i_to = 0; i_to < instr->link_count; i_to++) + if (instr->links[i_to] == old_to) + break; - /* Côté point de départ */ + assert((i_from < dest->link_count && i_to < instr->link_count) + || (i_from == dest->link_count && i_to == instr->link_count)); - new_dst.linked = dest; - new_dst.type = type; + status = (i_from < dest->link_count && i_to < instr->link_count); - ref_instr_link((&new_dst)); + if (status) + { + if ((i_from + 1) < dest->link_count) + memmove(&dest->links[i_from], &dest->links[i_from + 1], + (dest->link_count - i_from - 1) * sizeof(compact_ins_link_t)); - /* Ajout dans le respect d'une cohérence globale */ + dest->links = realloc(dest->links, --dest->link_count * sizeof(compact_ins_link_t)); - g_arch_instruction_lock_src(dest); - g_arch_instruction_lock_dest(instr); + if ((i_to + 1) < instr->link_count) + memmove(&instr->links[i_to], &instr->links[i_to + 1], + (instr->link_count - i_to - 1) * sizeof(compact_ins_link_t)); - add_item_to_flat_array(&dest->from, &new_src, sizeof(instr_link_t)); + instr->links = realloc(instr->links, --instr->link_count * sizeof(compact_ins_link_t)); - add_item_to_flat_array(&instr->to, &new_dst, sizeof(instr_link_t)); + } - g_arch_instruction_unlock_dest(instr); - g_arch_instruction_unlock_src(dest); + g_thick_object_unlock(G_THICK_OBJECT(dest)); + g_thick_object_unlock(G_THICK_OBJECT(instr)); } /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * +* Paramètres : instr = instruction dont les informations sont à manipuler. * * dest = ligne visée par la liaison (côté destination). * * old = ancien type de lien construit. * * new = nouveau type de lien à construire. * @@ -1199,67 +849,43 @@ void g_arch_instruction_link_with(GArchInstruction *instr, GArchInstruction *des bool g_arch_instruction_change_link(GArchInstruction *instr, GArchInstruction *dest, InstructionLinkType old, InstructionLinkType new) { bool result; /* Bilan à retourner */ - size_t count; /* Raccourci pour la lecture */ - size_t i; /* Boucle de parcours */ - instr_link_t *slink; /* Définition de source */ - instr_link_t *dlink; /* Définition de destination */ - - result = false; + compact_ins_link_t old_from; /* Ancien enregistrement #1 */ + compact_ins_link_t new_from; /* Nouvel enregistrement #1 */ + compact_ins_link_t old_to; /* Ancien enregistrement #2 */ + compact_ins_link_t new_to; /* Nouvel enregistrement #2 */ + uint16_t i_from; /* Boucle de parcours #1 */ + uint16_t i_to; /* Boucle de parcours #2 */ - /** - * Note : pour la récupération des liens de sources et de destinations, - * on n'utilise pas les fonctions g_arch_instruction_get_(source|destination)(), - * qui renvoient un pointeur non modifiable. - * - * On a en effet besoin de modifier le type de lien. - */ + old_from = MAKE_COMPACT_INS_LINK(COMPACT_INS_LINK_FROM, instr, old); + new_from = MAKE_COMPACT_INS_LINK(COMPACT_INS_LINK_FROM, instr, new); + old_to = MAKE_COMPACT_INS_LINK(COMPACT_INS_LINK_TO, dest, old); + new_to = MAKE_COMPACT_INS_LINK(COMPACT_INS_LINK_TO, dest, new); - g_arch_instruction_lock_src(dest); + g_thick_object_lock(G_THICK_OBJECT(instr)); + g_thick_object_lock(G_THICK_OBJECT(dest)); - /* Côté destination */ - - count = g_arch_instruction_count_sources(dest); - - for (i = 0; i < count; i++) - { - slink = get_flat_array_item(dest->from, i, sizeof(instr_link_t)); - - if (slink->linked == instr && slink->type == old) + for (i_from = 0; i_from < dest->link_count; i_from++) + if (dest->links[i_from] == old_from) break; - } - - if (i == count) - goto gaicl_exit; + for (i_to = 0; i_to < instr->link_count; i_to++) + if (instr->links[i_to] == old_to) + break; - /* Côté point de départ */ + assert((i_from < dest->link_count && i_to < instr->link_count) + || (i_from == dest->link_count && i_to == instr->link_count)); - count = g_arch_instruction_count_destinations(instr); + result = (i_from < dest->link_count && i_to < instr->link_count); - for (i = 0; i < count; i++) + if (result) { - dlink = get_flat_array_item(instr->to, i, sizeof(instr_link_t)); - - if (dlink->linked == dest && dlink->type == old) - break; - + dest->links[i_from] = new_from; + instr->links[i_to] = new_to; } - if (i == count) - goto gaicl_exit; - - /* Si les deux extrémités sont raccord... */ - - slink->type = new; - - dlink->type = new; - - result = true; - - gaicl_exit: - - g_arch_instruction_unlock_src(dest); + g_thick_object_unlock(G_THICK_OBJECT(dest)); + g_thick_object_unlock(G_THICK_OBJECT(instr)); return result; @@ -1280,116 +906,92 @@ bool g_arch_instruction_change_link(GArchInstruction *instr, GArchInstruction *d void g_arch_instruction_delete_all_links(GArchInstruction *instr) { - instr_link_t *link_src; /* Lien à supprimer #2 */ - GArchInstruction *other; /* Instruction de l'autre bout */ - size_t count; /* Quantié de liens présents */ - size_t i; /* Boucle de parcours */ - instr_link_t *link_dst; /* Lien à supprimer #1 */ + GArchInstruction *linked; /* Autre instruction liée */ + InstructionLinkType type; /* Type de liaison */ - /* Coté sources */ + g_thick_object_lock(G_THICK_OBJECT(instr)); - g_arch_instruction_lock_src(instr); - - while (count_flat_array_items(instr->from) > 0) + while (g_arch_instruction_count_src_links(instr) > 0) { - link_src = get_flat_array_item(instr->from, 0, sizeof(instr_link_t)); - - other = link_src->linked; + linked = g_arch_instruction_get_linked_source(instr, 0, &type); - g_arch_instruction_lock_dest(other); + g_thick_object_unlock(G_THICK_OBJECT(instr)); - count = count_flat_array_items(other->to); + g_arch_instruction_unlink(linked, instr, type); - for (i = 0; i < count; i++) - { - link_dst = get_flat_array_item(other->to, i, sizeof(instr_link_t)); + g_thick_object_lock(G_THICK_OBJECT(instr)); - if (link_dst->linked == instr && link_dst->type == link_src->type) - { - unref_instr_link(link_dst); - - rem_item_from_flat_array(&other->to, i, sizeof(instr_link_t)); - - break; - - } - - } - - assert(i < count); - - g_arch_instruction_unlock_dest(other); - - unref_instr_link(link_src); - - rem_item_from_flat_array(&instr->from, 0, sizeof(instr_link_t)); + unref_object(linked); } - g_arch_instruction_unlock_src(instr); - - /* Coté destinations */ - - g_arch_instruction_lock_dest(instr); - - while (count_flat_array_items(instr->to) > 0) + while (g_arch_instruction_count_dest_links(instr) > 0) { - link_dst = get_flat_array_item(instr->to, 0, sizeof(instr_link_t)); + linked = g_arch_instruction_get_linked_destination(instr, 0, &type); - other = link_dst->linked; + g_thick_object_unlock(G_THICK_OBJECT(instr)); - g_arch_instruction_lock_src(other); + g_arch_instruction_unlink(instr, linked, type); - count = count_flat_array_items(other->from); + g_thick_object_lock(G_THICK_OBJECT(instr)); - for (i = 0; i < count; i++) - { - link_src = get_flat_array_item(other->from, i, sizeof(instr_link_t)); + unref_object(linked); - if (link_src->linked == instr && link_src->type == link_dst->type) - { - unref_instr_link(link_src); - - rem_item_from_flat_array(&other->from, i, sizeof(instr_link_t)); + } - break; + g_thick_object_unlock(G_THICK_OBJECT(instr)); - } +} - } - assert(i < count); +/****************************************************************************** +* * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* dir = direction des liens à considérer. * +* * +* Description : Fournit la quantité d'instructions pointant vers une autre. * +* * +* Retour : Nombre de ces liens. * +* * +* Remarques : - * +* * +******************************************************************************/ - g_arch_instruction_unlock_src(other); +static size_t _g_arch_instruction_count_links(const GArchInstruction *instr, compact_ins_link_t dir) +{ + size_t result; /* Nombre de liens à renvoyer */ + uint16_t i; /* Boucle de parcours */ - unref_instr_link(link_dst); + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - rem_item_from_flat_array(&instr->to, 0, sizeof(instr_link_t)); + result = 0; - } + for (i = 0; i < instr->link_count; i++) + if (COMPACT_INS_LINK_DIR(instr->links[i]) == dir) + result++; - g_arch_instruction_unlock_dest(instr); + return result; } /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * +* Paramètres : instr = instruction dont les liens sont à consulter. * * * -* Description : Fournit la quantité d'instructions pointant vers une autre. * +* Description : Fournit la quantité d'instructions placées en source. * * * -* Retour : Nombre de ces origines. * +* Retour : Nombre de ces liens. * * * * Remarques : - * * * ******************************************************************************/ -size_t g_arch_instruction_count_sources(const GArchInstruction *instr) +size_t g_arch_instruction_count_src_links(const GArchInstruction *instr) { size_t result; /* Nombre de liens à renvoyer */ - result = count_flat_array_items(instr->from); + result = _g_arch_instruction_count_links(instr, COMPACT_INS_LINK_FROM); return result; @@ -1398,24 +1000,21 @@ size_t g_arch_instruction_count_sources(const GArchInstruction *instr) /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * -* index = indice de l'élément à retrouver. * +* Paramètres : instr = instruction dont les liens sont à consulter. * * * -* Description : Fournit les détails d'une origine d'une instruction donnée. * +* Description : Fournit la quantité d'instructions placées en destination. * * * -* Retour : Lien déterminé vers une instruction d'origine. * +* Retour : Nombre de ces liens. * * * * Remarques : - * * * ******************************************************************************/ -const instr_link_t *g_arch_instruction_get_source(GArchInstruction *instr, size_t index) +size_t g_arch_instruction_count_dest_links(const GArchInstruction *instr) { - instr_link_t *result; /* Détails présents à renvoyer */ - - result = get_flat_array_item(instr->from, index, sizeof(instr_link_t)); + size_t result; /* Nombre de liens à renvoyer */ - ref_instr_link(result); + result = _g_arch_instruction_count_links(instr, COMPACT_INS_LINK_TO); return result; @@ -1424,43 +1023,47 @@ const instr_link_t *g_arch_instruction_get_source(GArchInstruction *instr, size_ /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * -* count = quantié de liens présents. [OUT] * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* index = indice de l'élément à retrouver. * +* dir = direction des liens à considérer. * +* type = type de lien enregistré. [OUT] * * * -* Description : Fournit tous les détails d'origine d'une instruction donnée. * +* Description : Fournit les détails d'un lien donné avec une instruction. * * * -* Retour : Liens vers des instructions d'origine à libérer. * +* Retour : Autre instruction pointée par l'instruction, voire NULL. * * * * Remarques : - * * * ******************************************************************************/ -instr_link_t *g_arch_instruction_get_sources(GArchInstruction *instr, size_t *count) +static GArchInstruction *_g_arch_instruction_get_linked_instruction(const GArchInstruction *instr, size_t index, compact_ins_link_t dir, InstructionLinkType *type) { - instr_link_t *result; /* Détails présents à renvoyer */ - size_t i; /* Boucle de parcours */ - const instr_link_t *link; /* Lien à fournir */ - - g_arch_instruction_lock_src(instr); + GArchInstruction *result; /* Instance ciblée à renvoyer */ + uint16_t i; /* Boucle de parcours */ - *count = g_arch_instruction_count_sources(instr); + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - if (*count == 0) - result = NULL; + result = NULL; + *type = ILT_COUNT; - else + for (i = 0; i < instr->link_count; i++) { - result = (instr_link_t *)malloc(*count * sizeof(instr_link_t)); + if (COMPACT_INS_LINK_DIR(instr->links[i]) != dir) + continue; - for (i = 0; i < *count; i++) + if (index == 0) { - link = g_arch_instruction_get_source(instr, i); - memcpy(&result[i], link, sizeof(instr_link_t)); + result = COMPACT_INS_LINK_PTR(instr->links[i]); + *type = COMPACT_INS_LINK_TYPE(instr->links[i]); } + else + index--; + } - g_arch_instruction_unlock_src(instr); + if (result != NULL) + ref_object(result); return result; @@ -1469,21 +1072,23 @@ instr_link_t *g_arch_instruction_get_sources(GArchInstruction *instr, size_t *co /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * +* Paramètres : instr = instruction dont les liens sont à consulter. * +* index = indice de l'élément à retrouver. * +* type = type de lien enregistré. [OUT] * * * -* Description : Donne le nombre d'instructions non naturellement suivantes. * +* Description : Fournit les détails d'une source donnée d'une instruction. * * * -* Retour : Nombre de ces destinations. * +* Retour : Autre instruction pointée par l'instruction, voire NULL. * * * * Remarques : - * * * ******************************************************************************/ -size_t g_arch_instruction_count_destinations(const GArchInstruction *instr) +GArchInstruction *g_arch_instruction_get_linked_source(const GArchInstruction *instr, size_t index, InstructionLinkType *type) { - size_t result; /* Nombre de liens à renvoyer */ + GArchInstruction *result; /* Instance ciblée à renvoyer */ - result = count_flat_array_items(instr->to); + result = _g_arch_instruction_get_linked_instruction(instr, index, COMPACT_INS_LINK_FROM, type); return result; @@ -1492,71 +1097,54 @@ size_t g_arch_instruction_count_destinations(const GArchInstruction *instr) /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * +* Paramètres : instr = instruction dont les liens sont à consulter. * * index = indice de l'élément à retrouver. * +* type = type de lien enregistré. [OUT] * * * -* Description : Fournit les détails d'une destination d'une instruction. * +* Description : Fournit les détails d'une destination donnée d'une instruct. * * * -* Retour : Lien déterminé vers une instruction de destination. * +* Retour : Autre instruction pointée par l'instruction, voire NULL. * * * * Remarques : - * * * ******************************************************************************/ -const instr_link_t *g_arch_instruction_get_destination(GArchInstruction *instr, size_t index) +GArchInstruction *g_arch_instruction_get_linked_destination(const GArchInstruction *instr, size_t index, InstructionLinkType *type) { - instr_link_t *result; /* Détails présents à renvoyer */ - - result = get_flat_array_item(instr->to, index, sizeof(instr_link_t)); + GArchInstruction *result; /* Instance ciblée à renvoyer */ - ref_instr_link(result); + result = _g_arch_instruction_get_linked_instruction(instr, index, COMPACT_INS_LINK_TO, type); return result; } + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DES OPERANDES */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * -* type = type de lien recherché. * +* Paramètres : instr = instance à consulter. * * * -* Description : Fournit la destination d'une instruction et d'un type donné. * +* Description : Indique la quantité d'opérandes présents dans l'instruction. * * * -* Retour : Instruction de destination trouvée ou NULL. * +* Retour : Nombre d'opérandes attachés. * * * * Remarques : - * * * ******************************************************************************/ -GArchInstruction *g_arch_instruction_get_given_destination(GArchInstruction *instr, InstructionLinkType type) +size_t g_arch_instruction_count_operands(const GArchInstruction *instr) { - GArchInstruction *result; /* Résultat à remonter */ - size_t count; /* Nombre de liens à parcourir */ - size_t i; /* Boucle de parcours */ - const instr_link_t *dest; /* Destination à étudier */ - - result = NULL; - - g_arch_instruction_lock_dest(instr); - - count = g_arch_instruction_count_destinations(instr); - - for (i = 0; i < count && result == NULL; i++) - { - dest = g_arch_instruction_get_destination(instr, i); - - if (dest->type == type) - { - result = dest->linked; - g_object_ref(G_OBJECT(result)); - } + size_t result; /* Décompte à retourner */ - unref_instr_link(dest); + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - } - - g_arch_instruction_unlock_dest(instr); + result = count_flat_array_items(instr->operands); return result; @@ -1565,56 +1153,45 @@ GArchInstruction *g_arch_instruction_get_given_destination(GArchInstruction *ins /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter. * -* count = quantié de liens présents. [OUT] * +* Paramètres : instr = instance à mettre à jour. * +* operand = instruction à venir associer. * * * -* Description : Fournit tous les détails de destination d'une instruction. * +* Description : Attache un opérande supplémentaire à une instruction. * * * -* Retour : Liens vers des instructions de destination à libérer. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -instr_link_t *g_arch_instruction_get_destinations(GArchInstruction *instr, size_t *count) +void g_arch_instruction_attach_operand(GArchInstruction *instr, GArchOperand *operand) { - instr_link_t *result; /* Détails présents à renvoyer */ - size_t i; /* Boucle de parcours */ - const instr_link_t *link; /* Lien à fournir */ - - g_arch_instruction_lock_dest(instr); - - *count = g_arch_instruction_count_destinations(instr); + GSingletonFactory *factory; /* Unise à instances uniques */ + GSingletonCandidate *singleton; /* Instance retenue */ + GArchOperand *stored; /* Forme d'opérande conservée */ - if (*count == 0) - result = NULL; + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - else - { - result = (instr_link_t *)malloc(*count * sizeof(instr_link_t)); + factory = get_operands_factory(); - for (i = 0; i < *count; i++) - { - link = g_arch_instruction_get_destination(instr, i); - memcpy(&result[i], link, sizeof(instr_link_t)); - } + singleton = g_singleton_factory_get_instance(factory, G_SINGLETON_CANDIDATE(operand)); - } + unref_object(factory); - g_arch_instruction_unlock_dest(instr); + stored = G_ARCH_OPERAND(singleton); - return result; + add_item_to_flat_array(&instr->operands, &stored, sizeof(GArchOperand *)); } /****************************************************************************** * * -* Paramètres : instr = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * +* Paramètres : instr = instance à mettre à jour. * +* old = ancienne opérande à détacher. * +* new = nouvelle opérande à attacher. * * * -* Description : Charge un contenu depuis une mémoire tampon. * +* Description : Remplace un opérande d'une instruction par un autre. * * * * Retour : Bilan de l'opération. * * * @@ -1622,40 +1199,47 @@ instr_link_t *g_arch_instruction_get_destinations(GArchInstruction *instr, size_ * * ******************************************************************************/ -static bool g_arch_instruction_load_destinations(GArchInstruction *instr, GObjectStorage *storage, packed_buffer_t *pbuf) +bool g_arch_instruction_replace_operand(GArchInstruction *instr, GArchOperand *old, GArchOperand *new) { bool result; /* Bilan à retourner */ - uleb128_t count; /* Nombre de liens à charger */ - uleb128_t i; /* Boucle de parcours */ - GArchInstruction *linked; /* Lien vers une instruction */ - uleb128_t type; /* Valeur ULEB128 à charger */ + size_t count; /* Nombre d'opérandes en place */ + size_t i; /* Boucle de parcours */ + GArchOperand *op; /* Opérande à manipuler */ + GSingletonFactory *factory; /* Unise à instances uniques */ + GSingletonCandidate *singleton; /* Instance retenue */ + GArchOperand *stored; /* Forme d'opérande conservée */ - g_arch_instruction_lock_dest(instr); + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - result = unpack_uleb128(&count, pbuf); + result = false; - for (i = 0; i < count && result; i++) + count = g_arch_instruction_count_operands(instr); + + for (i = 0; i < count && !result; i++) { - linked = G_ARCH_INSTRUCTION(g_object_storage_unpack_object(storage, "instructions", pbuf)); - if (linked == NULL) - { - result = false; - break; - } + op = g_arch_instruction_get_operand(instr, i); - result = unpack_uleb128(&type, pbuf); - if (!result) - { - g_object_unref(G_OBJECT(linked)); - break; - } + result = (op == old); - g_arch_instruction_link_with(instr, linked, type); - g_object_unref(G_OBJECT(linked)); + unref_object(op); } - g_arch_instruction_unlock_dest(instr); + if (result) + { + factory = get_operands_factory(); + + singleton = g_singleton_factory_get_instance(factory, G_SINGLETON_CANDIDATE(new)); + + unref_object(factory); + + stored = G_ARCH_OPERAND(singleton); + + rpl_item_in_flat_array(instr->operands, i - 1, &stored, sizeof(GArchOperand *)); + + unref_object(old); + + } return result; @@ -1664,11 +1248,10 @@ static bool g_arch_instruction_load_destinations(GArchInstruction *instr, GObjec /****************************************************************************** * * -* Paramètres : instr = instruction dont les informations sont à consulter.* -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* Paramètres : instr = instance à mettre à jour. * +* target = instruction à venir dissocier. * * * -* Description : Sauvegarde toutes les destinations d'une instruction. * +* Description : Détache un opérande liée d'une instruction. * * * * Retour : Bilan de l'opération. * * * @@ -1676,87 +1259,67 @@ static bool g_arch_instruction_load_destinations(GArchInstruction *instr, GObjec * * ******************************************************************************/ -bool g_arch_instruction_store_destinations(GArchInstruction *instr, GObjectStorage *storage, packed_buffer_t *pbuf) +bool g_arch_instruction_detach_operand(GArchInstruction *instr, GArchOperand *target) { bool result; /* Bilan à retourner */ - size_t count; /* Nombre d'éléments à traiter */ - size_t kept; /* Nombre de liens conservés */ + size_t count; /* Nombre d'opérandes en place */ size_t i; /* Boucle de parcours */ - const instr_link_t *link; /* Lien vers une instruction */ - - g_arch_instruction_lock_dest(instr); + GArchOperand *op; /* Opérande à manipuler */ - count = g_arch_instruction_count_destinations(instr); + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - /** - * Le type de lien ILT_REF n'est mis en place que lors de la création - * d'opérandes de type G_TYPE_TARGET_OPERAND, et sera donc remis en place - * dynamiquement lors de la restauration de ces derniers. - */ + result = false; - kept = 0; + count = g_arch_instruction_count_operands(instr); - for (i = 0; i < count; i++) + for (i = 0; i < count && !result; i++) { - link = g_arch_instruction_get_destination(instr, i); + op = g_arch_instruction_get_operand(instr, i); - if (link->type != ILT_REF) - kept++; + result = (op == target); - unref_instr_link(link); + unref_object(op); } - result = pack_uleb128((uleb128_t []){ kept }, pbuf); - - for (i = 0; i < count && result; i++) + if (result) { - link = g_arch_instruction_get_destination(instr, i); - - if (link->type != ILT_REF) - { - result = g_object_storage_pack_object(storage, "instructions", - G_SERIALIZABLE_OBJECT(link->linked), pbuf); - - if (result) - result = pack_uleb128((uleb128_t []){ link->type }, pbuf); - - } + rem_item_from_flat_array(&instr->operands, i - 1, sizeof(GArchOperand *)); - unref_instr_link(link); + unref_object(target); } - g_arch_instruction_unlock_dest(instr); - return result; } - -/* ---------------------------------------------------------------------------------- */ -/* CONVERSIONS DU FORMAT DES INSTRUCTIONS */ -/* ---------------------------------------------------------------------------------- */ - - /****************************************************************************** * * -* Paramètres : instr = instruction d'assemblage à consulter. * +* Paramètres : instr = instance à consulter. * +* index = indice de l'opérande concerné. * * * -* Description : Fournit le nom humain de l'instruction manipulée. * +* Description : Fournit un opérande donné d'une instruction. * * * -* Retour : Mot clef de bas niveau. * +* Retour : Opérande trouvée. * * * * Remarques : - * * * ******************************************************************************/ -const char *g_arch_instruction_get_keyword(GArchInstruction *instr) +GArchOperand *g_arch_instruction_get_operand(const GArchInstruction *instr, size_t index) { - const char *result; /* Désignation à retourner */ + GArchOperand *result; /* Opérande à retourner */ + GArchOperand **ptr; /* Adresse dans le tableau */ + + assert(g_thick_object_check_lock(G_THICK_OBJECT(instr))); - result = G_ARCH_INSTRUCTION_GET_CLASS(instr)->get_keyword(instr); + ptr = get_flat_array_item(instr->operands, index, sizeof(GArchOperand *)); + + result = *ptr; + + ref_object(result); return result; @@ -1765,450 +1328,396 @@ const char *g_arch_instruction_get_keyword(GArchInstruction *instr) /****************************************************************************** * * -* Paramètres : instr = instruction d'assemblage à consulter. * +* Paramètres : instr = instance à consulter. * +* target = instruction à venir retrouver. * * * -* Description : Construit un petit résumé concis de l'instruction. * +* Description : Détermine le chemin conduisant à un opérande. * * * -* Retour : Chaîne de caractères à libérer après usage ou NULL. * +* Retour : Chemin d'accès à l'opérande ou NULL en cas d'absence. * * * * Remarques : - * * * ******************************************************************************/ -char *g_arch_instruction_build_tooltip(const GArchInstruction *instr) +char *g_arch_instruction_find_operand_path(GArchInstruction *instr, const GArchOperand *target) { - char *result; /* Description à retourner */ - GArchInstructionClass *class; /* Classe des instructions */ + char *result; /* Chemin à retourner */ + size_t count; /* Nombre d'opérandes en place */ + size_t i; /* Boucle de parcours */ + GArchOperand *op; /* Opérande à manipuler */ + int ret; /* Bilan d'une construction */ + char *sub_path; /* Sous-chemin emprunté */ - class = G_ARCH_INSTRUCTION_GET_CLASS(instr); + result = NULL; - if (class->build_tooltip != NULL) - result = class->build_tooltip(instr); + g_thick_object_lock(G_THICK_OBJECT(instr)); - else - result = NULL; + count = g_arch_instruction_count_operands(instr); - return result; + /* Première passe : accès direct */ -} + for (i = 0; i < count && result == NULL; i++) + { + op = g_arch_instruction_get_operand(instr, i); + if (op == target) + { + ret = asprintf(&result, "%zu", i); + if (ret == -1) + { + LOG_ERROR_N("asprintf"); + result = NULL; + } + } -/****************************************************************************** -* * -* Paramètres : instr = instruction d'assemblage à consulter. * -* * -* Description : Fournit une description pour l'instruction manipulée. * -* * -* Retour : Chaîne de caractères avec balises éventuelles. * -* * -* Remarques : - * -* * -******************************************************************************/ + unref_object(op); -const char *g_arch_instruction_get_description(const GArchInstruction *instr) -{ - const char *result; /* Description à retourner */ + } - result = G_ARCH_INSTRUCTION_GET_CLASS(instr)->get_desc(instr); + /* Seconde passe : accès profond */ - return result; - -} + for (i = 0; i < count && result == NULL; i++) + { + op = g_arch_instruction_get_operand(instr, i); + sub_path = NULL;//g_arch_operand_find_inner_operand_path(op, target); + if (sub_path != NULL) + { + ret = asprintf(&result, "%zu:%s", i, sub_path); + if (ret == -1) + { + LOG_ERROR_N("asprintf"); + result = NULL; + } -/* ---------------------------------------------------------------------------------- */ -/* OFFRE DE CAPACITES DE GENERATION */ -/* ---------------------------------------------------------------------------------- */ + free(sub_path); + } -/****************************************************************************** -* * -* Paramètres : instr = générateur à consulter. * -* * -* Description : Indique le nombre de ligne prêtes à être générées. * -* * -* Retour : Nombre de lignes devant apparaître au final. * -* * -* Remarques : - * -* * -******************************************************************************/ + unref_object(op); -static size_t g_arch_instruction_count_lines(const GArchInstruction *instr) -{ - return 1; + } -} + g_thick_object_unlock(G_THICK_OBJECT(instr)); + return result; -#ifdef INCLUDE_GTK_SUPPORT +} /****************************************************************************** * * -* Paramètres : instr = générateur à consulter. * -* x = position géographique sur la ligne concernée. * -* index = indice de cette même ligne dans le tampon global. * -* repeat = indice d'utilisations successives du générateur. * -* cursor = emplacement à constituer. [OUT] * +* Paramètres : instr = instance à consulter. * +* path = chemin d'accès à un opérande à retrouver. * * * -* Description : Retrouve l'emplacement correspondant à une position donnée. * +* Description : Obtient l'opérande correspondant à un chemin donné. * * * -* Retour : - * +* Retour : Opérande trouvé ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ -static void g_arch_instruction_compute_cursor(const GArchInstruction *instr, gint x, size_t index, size_t repeat, GLineCursor **cursor) +GArchOperand *g_arch_instruction_get_operand_from_path(GArchInstruction *instr, const char *path) { - *cursor = g_binary_cursor_new(); + GArchOperand *result; /* Opérande trouvée à renvoyer */ + size_t index; /* Indice de l'opérande visé */ + char *end; /* Poursuite du parcours ? */ + GArchOperand *found; /* Opérande trouvé */ - g_binary_cursor_update(G_BINARY_CURSOR(*cursor), get_mrange_addr(&instr->range)); + result = NULL; -} + g_thick_object_lock(G_THICK_OBJECT(instr)); + /* Recherche au premier niveau */ -/****************************************************************************** -* * -* Paramètres : instr = générateur à consulter. * -* index = indice de cette même ligne dans le tampon global. * -* repeat = indice d'utilisations successives du générateur. * -* cursor = emplacement à analyser. * -* * -* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * -* * -* Retour : Bilan de la détermination, utilisable en comparaisons. * -* * -* Remarques : - * -* * -******************************************************************************/ + index = strtoul(path, &end, 10); -static int g_arch_instruction_contain_cursor(const GArchInstruction *instr, size_t index, size_t repeat, const GLineCursor *cursor) -{ - int result; /* Conclusion à retourner */ - vmpa2t addr; /* Autre emplacement à comparer*/ + if ((index == ULONG_MAX && errno == ERANGE) || (index == 0 && errno == EINVAL)) + { + LOG_ERROR_N("strtoul"); + goto done; + } + + found = g_arch_instruction_get_operand(instr, index); + if (found == NULL) goto done; + + if (*end == '\0') + { + result = found; + goto done; + } + + /* Recherche en profondeur */ + + assert(*end == ':'); + + result = NULL;//g_arch_operand_get_inner_operand_from_path(found, end + 1); - assert(G_IS_BINARY_CURSOR(cursor)); + unref_object(found); - g_binary_cursor_retrieve(G_BINARY_CURSOR(cursor), &addr); + done: - result = cmp_mrange_with_vmpa(&instr->range, &addr); + g_thick_object_unlock(G_THICK_OBJECT(instr)); return result; } -#endif + +/* ---------------------------------------------------------------------------------- */ +/* MECANISMES DE CONSERVATION ET RESTAURATION */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : instr = générateur à consulter. * -* index = indice de cette même ligne dans le tampon global. * -* repeat = indice d'utilisations successives du générateur. * +* Paramètres : object = élément GLib à constuire. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * * * -* Description : Renseigne sur les propriétés liées à un générateur. * +* Description : Charge un objet depuis un flux de données. * * * -* Retour : Propriétés particulières associées. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static BufferLineFlags g_arch_instruction_get_flags2(const GArchInstruction *instr, size_t index, size_t repeat) +static bool g_arch_instruction_load(GSerializableObject *object, GObjectStorage *storage, int fd) { - return BLF_HAS_CODE; + bool result; /* Bilan à retourner */ + uleb128_t extra; /* Données embarquées */ + + /* Propriétés internes */ + + result = load_uleb128(&extra, fd); + + if (result) + g_thick_object_set_extra(G_THICK_OBJECT(object), extra); + + /* Liaisons avec d'autres instructions */ + + return result; } /****************************************************************************** * * -* Paramètres : instr = instruction d'assemblage à représenter. * -* line = ligne de rendu à compléter. * -* index = indice de cette même ligne dans le tampon global. * -* repeat = indice d'utilisations successives du générateur. * -* content = éventuel contenu binaire brut à imprimer. * +* Paramètres : object = élément GLib à consulter. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * * * -* Description : Ajoute à un tampon GLib le contenu de l'instance spécifiée. * +* Description : Sauvegarde un objet dans un flux de données. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void _g_arch_instruction_print(GArchInstruction *instr, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content) +static bool g_arch_instruction_store(const GSerializableObject *object, GObjectStorage *storage, int fd) { - const char *key; /* Mot clef principal */ - size_t klen; /* Taille de ce mot clef */ - size_t count; /* Nombre d'opérandes en place */ + bool result; /* Bilan à retourner */ + GArchInstruction *instr; /* Version spécialisée */ + size_t src_count; /* Quantité de sources */ + size_t dest_count; /* Quantité de destinations */ + off64_t *ins_offsets; /* Emplacements d'instructions */ size_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à manipuler */ + GArchInstruction *linked; /* Instruction liée */ + size_t op_count; /* Quantité d'opérandes */ + off64_t *op_offsets; /* Emplacements d'opérandes */ + GArchOperand *op; /* Opérande à traiter */ + guint extra; /* Données embarquées */ + InstructionLinkType type; /* Type de lien */ - g_buffer_line_fill_phys(line, DLC_PHYSICAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&instr->range)); + assert(g_thick_object_check_lock(G_THICK_OBJECT(object))); - g_buffer_line_fill_virt(line, DLC_VIRTUAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&instr->range)); + /* Préparation des références aux instructions liées */ - g_buffer_line_fill_content(line, DLC_BINARY, content, &instr->range, VMPA_NO_PHYSICAL); + instr = G_ARCH_INSTRUCTION(object); - /* Instruction proprement dite */ + src_count = g_arch_instruction_count_src_links(instr); + dest_count = g_arch_instruction_count_dest_links(instr); - key = g_arch_instruction_get_keyword(instr); - klen = strlen(key); + ins_offsets = malloc((src_count + dest_count) * sizeof(off64_t)); - g_buffer_line_append_text(line, DLC_ASSEMBLY_HEAD, key, klen, RTT_INSTRUCTION, G_OBJECT(instr)); + for (i = 0; i < src_count && result; i++) + { + linked = g_arch_instruction_get_linked_source(instr, i, (InstructionLinkType []) { 0 }); - /* Liste des opérandes */ + result = g_object_storage_store_object(storage, "instructions", + G_SERIALIZABLE_OBJECT(linked), &ins_offsets[i]); - g_arch_instruction_lock_operands(instr); + unref_object(linked); - count = _g_arch_instruction_count_operands(instr); + } - if (count > 0) + for (i = 0; i < dest_count && result; i++) { - op = _g_arch_instruction_get_operand(instr, 0); - g_arch_operand_print(op, line); - g_object_unref(G_OBJECT(op)); + linked = g_arch_instruction_get_linked_destination(instr, i, (InstructionLinkType []) { 0 }); - for (i = 1; i < count; i++) - { - g_buffer_line_append_text(line, DLC_ASSEMBLY, ",", 1, RTT_PUNCT, NULL); - g_buffer_line_append_text(line, DLC_ASSEMBLY, " ", 1, RTT_RAW, NULL); + result = g_object_storage_store_object(storage, "instructions", + G_SERIALIZABLE_OBJECT(linked), &ins_offsets[src_count + i]); - op = _g_arch_instruction_get_operand(instr, i); + unref_object(linked); - g_arch_operand_print(op, line); + } - g_object_unref(G_OBJECT(op)); + if (!result) + goto exit_with_ins_off; - } + /* Préparation des références aux opérandes embarqués */ - } + op_count = g_arch_instruction_count_operands(instr); - g_arch_instruction_unlock_operands(instr); + op_offsets = malloc(op_count * sizeof(off64_t)); -} + for (i = 0; i < op_count && result; i++) + { + op = g_arch_instruction_get_operand(instr, i); + result = g_object_storage_store_object(storage, "operandss", + G_SERIALIZABLE_OBJECT(op), &op_offsets[i]); -/****************************************************************************** -* * -* Paramètres : instr = générateur à utiliser pour l'impression. * -* line = ligne de rendu à compléter. * -* index = indice de cette même ligne dans le tampon global. * -* repeat = indice d'utilisations successives du générateur. * -* content = éventuel contenu binaire brut à imprimer. * -* * -* Description : Imprime dans une ligne de rendu le contenu représenté. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + unref_object(op); -static void g_arch_instruction_print(GArchInstruction *instr, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content) -{ - G_ARCH_INSTRUCTION_GET_CLASS(instr)->print(instr, line, index, repeat, content); + } -} + if (!result) + goto exit_with_op_off; + /* Propriétés internes */ + extra = g_thick_object_get_extra(G_THICK_OBJECT(object)); -/* ---------------------------------------------------------------------------------- */ -/* CONSERVATION ET RECHARGEMENT DES DONNEES */ -/* ---------------------------------------------------------------------------------- */ + result = store_uleb128((uleb128_t []) { extra }, fd); + if (!result) goto exit; + /* Liaisons avec d'autres instructions */ -/****************************************************************************** -* * -* Paramètres : instr = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * -* * -* Description : Charge un contenu depuis une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ + instr = G_ARCH_INSTRUCTION(object); -static bool _g_arch_instruction_load(GArchInstruction *instr, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - instr_extra_data_t *extra; /* Données insérées à consulter*/ - uleb128_t value; /* Valeur ULEB128 à charger */ - uleb128_t count; /* Nombre d'éléments à traiter */ - uleb128_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à traiter */ + src_count = g_arch_instruction_count_src_links(instr); + dest_count = g_arch_instruction_count_dest_links(instr); - extra = GET_ARCH_INSTR_EXTRA(instr); + result = store_uleb128((uleb128_t []) { src_count }, fd); + if (!result) goto exit; - LOCK_GOBJECT_EXTRA(extra); + result = store_uleb128((uleb128_t []) { dest_count }, fd); + if (!result) goto exit; - result = unpack_uleb128(&value, pbuf); + for (i = 0; i < src_count && result; i++) + { + linked = g_arch_instruction_get_linked_source(instr, i, &type); - if (result) - extra->uid = value; + result = store_uleb128((uleb128_t []) { type }, fd); - if (result) - { - result = unpack_uleb128(&value, pbuf); + unref_object(linked); if (result) - extra->flags = value; + result = store_uleb128((uleb128_t []) { ins_offsets[i] }, fd); } - UNLOCK_GOBJECT_EXTRA(extra); - - if (result) - result = unpack_mrange(&instr->range, pbuf); - - if (result) + for (i = 0; i < dest_count && result; i++) { - result = unpack_uleb128(&count, pbuf); + linked = g_arch_instruction_get_linked_destination(instr, i, &type); - for (i = 0; i < count && result; i++) - { - op = G_ARCH_OPERAND(g_object_storage_unpack_object(storage, "operands", pbuf)); - result = (op != NULL); + result = store_uleb128((uleb128_t []) { type }, fd); - if (result) - g_arch_instruction_attach_extra_operand(instr, op); + unref_object(linked); - } + if (result) + result = store_uleb128((uleb128_t []) { ins_offsets[src_count + i] }, fd); } - if (result) - result = g_arch_instruction_load_destinations(instr, storage, pbuf); - - return result; + /* Opérandes embarqués */ -} + result = store_uleb128((uleb128_t []) { op_count }, fd); + if (!result) goto exit; + for (i = 0; i < op_count && result; i++) + result = store_uleb128((uleb128_t []) { op_offsets[i] }, fd); -/****************************************************************************** -* * -* Paramètres : instr = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * -* * -* Description : Charge un contenu depuis une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ + exit: + exit_with_op_off: -static bool g_arch_instruction_load(GArchInstruction *instr, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - GArchInstructionClass *class; /* Classe à activer */ + free(op_offsets); - class = G_ARCH_INSTRUCTION_GET_CLASS(instr); + exit_with_ins_off: - result = class->load(instr, storage, pbuf); + free(ins_offsets); return result; } -/****************************************************************************** -* * -* Paramètres : instr = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * -* * -* Description : Sauvegarde un contenu dans une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ -static bool _g_arch_instruction_store(GArchInstruction *instr, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - instr_extra_data_t *extra; /* Données insérées à consulter*/ - size_t count; /* Nombre d'éléments à traiter */ - size_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à traiter */ - - extra = GET_ARCH_INSTR_EXTRA(instr); - LOCK_GOBJECT_EXTRA(extra); - result = pack_uleb128((uleb128_t []){ extra->uid }, pbuf); - if (result) - result = pack_uleb128((uleb128_t []){ extra->flags }, pbuf); - UNLOCK_GOBJECT_EXTRA(extra); - if (result) - result = pack_mrange(&instr->range, pbuf); - if (result) - { - g_arch_instruction_lock_operands(instr); - count = _g_arch_instruction_count_operands(instr); - result = pack_uleb128((uleb128_t []){ count }, pbuf); - for (i = 0; i < count && result; i++) - { - op = _g_arch_instruction_get_operand(instr, i); - result = g_object_storage_pack_object(storage, "operands", G_SERIALIZABLE_OBJECT(op), pbuf); - g_object_unref(G_OBJECT(op)); - } - g_arch_instruction_unlock_operands(instr); - } - if (result) - result = g_arch_instruction_store_destinations(instr, storage, pbuf); +#if 0 - return result; -} /****************************************************************************** * * -* Paramètres : instr = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* Paramètres : instr = instruction quelconque à traiter. * +* type = type de procédure à utiliser. * +* proc = représentation de l'architecture utilisée. * +* context = contexte associé à la phase de désassemblage. * +* format = accès aux données du binaire d'origine. * * * -* Description : Sauvegarde un contenu dans une mémoire tampon. * +* Description : Complète un désassemblage accompli pour une instruction. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_arch_instruction_store(GArchInstruction *instr, GObjectStorage *storage, packed_buffer_t *pbuf) +void g_arch_instruction_call_hook(GArchInstruction *instr, InstrProcessHook type, GArchProcessor *proc, GProcContext *context, GExeFormat *format) { - bool result; /* Bilan à retourner */ - GArchInstructionClass *class; /* Classe à activer */ + GArchInstructionClass *class; /* Classe des instructions */ class = G_ARCH_INSTRUCTION_GET_CLASS(instr); - result = class->store(instr, storage, pbuf); - - return result; + if (class->call_hook != NULL) + class->call_hook(instr, type, proc, context, format); } + + + + + +#endif + + + + + + + + + + diff --git a/src/arch/instruction.h b/src/arch/instruction.h index 3c9c149..98bc73e 100644 --- a/src/arch/instruction.h +++ b/src/arch/instruction.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * instruction.h - prototypes pour la gestion générique des instructions * - * Copyright (C) 2008-2020 Cyrille Bagard + * Copyright (C) 2008-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,194 +25,118 @@ #define _ARCH_INSTRUCTION_H -#include <sys/types.h> +#include <stdbool.h> +#include <stdint.h> -#include "context.h" #include "operand.h" -#include "register.h" #include "vmpa.h" -#include "../analysis/type.h" -#include "../common/packed.h" -#include "../format/executable.h" +#include "../glibext/helpers.h" +#include "../glibext/portion.h" -#define G_TYPE_ARCH_INSTRUCTION g_arch_instruction_get_type() -#define G_ARCH_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_arch_instruction_get_type(), GArchInstruction)) -#define G_IS_ARCH_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_arch_instruction_get_type())) -#define G_ARCH_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_ARCH_INSTRUCTION, GArchInstructionClass)) -#define G_IS_ARCH_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_ARCH_INSTRUCTION)) -#define G_ARCH_INSTRUCTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_ARCH_INSTRUCTION, GArchInstructionClass)) +/* ----------------------- DEFINITION GENERIQUE D'INSTRUCTION ----------------------- */ -/* Définition générique d'une instruction d'architecture (instance) */ -typedef struct _GArchInstruction GArchInstruction; +#define G_TYPE_ARCH_INSTRUCTION (g_arch_instruction_get_type()) -/* Définition générique d'une instruction d'architecture (classe) */ -typedef struct _GArchInstructionClass GArchInstructionClass; +DECLARE_GTYPE(GArchInstruction, g_arch_instruction, G, ARCH_INSTRUCTION); -/* Drapeaux pour informations complémentaires */ - -#define AIF_USER_BIT 4 - -typedef enum _ArchInstrFlag -{ - AIF_NONE = (0 << 0), /* Aucune information */ - AIF_ROUTINE_START = (1 << 0), /* Début de routine */ - AIF_RETURN_POINT = (1 << 1), /* Retour de fonction appelée */ - AIF_COND_RETURN_POINT = (1 << 2), /* Retour éventuel de fonction */ - AIF_CALL = (1 << 3), /* Instruction d'appel */ - - AIF_LOW_USER = (1 << AIF_USER_BIT), /* Premier bit disponible */ - AIF_HIGH_USER = (1 << 7), /* Dernier bit disponible */ - -} ArchInstrFlag; - /* Type pour les types d'instructions */ typedef uint16_t itid_t; -/* Types de crochet de traitement */ -typedef enum _InstrProcessHook -{ - IPH_FETCH, /* Itinéraire de désassemblage */ - IPH_LINK, /* Edition des liens */ - IPH_POST, /* Résolution des symboles */ - - IPH_COUNT - -} InstrProcessHook; - - -/* Indique le type défini pour une instruction d'architecture. */ -GType g_arch_instruction_get_type(void); +/* Fournit l'identifiant correspondant à un type d'instructions. */ +itid_t g_arch_instruction_get_type_id(const GArchInstruction *); /* Indique l'encodage d'une instruction de façon détaillée. */ -const char *g_arch_instruction_get_encoding(const GArchInstruction *); - -/* Ajoute une information complémentaire à une instruction. */ -bool g_arch_instruction_set_flag(GArchInstruction *, ArchInstrFlag); +char *g_arch_instruction_get_encoding(const GArchInstruction *); -/* Retire une information complémentaire à une instruction. */ -bool g_arch_instruction_unset_flag(GArchInstruction *, ArchInstrFlag); - -/* Détermine si une instruction possède un fanion particulier. */ -bool g_arch_instruction_has_flag(const GArchInstruction *, ArchInstrFlag); +/* Fournit le nom humain de l'instruction manipulée. */ +char *g_arch_instruction_get_keyword(const GArchInstruction *); -/* Fournit les informations complémentaires d'une instruction. */ -ArchInstrFlag g_arch_instruction_get_flags(const GArchInstruction *); -/* Définit l'identifiant unique pour un ensemble d'instructions. */ -void g_arch_instruction_set_unique_id(GArchInstruction *, itid_t); + /* Type de masques pour les encodages d'instructions */ +typedef enum _InstructionBytesMask +{ + /** + * Correspond aux bits fixes : pas de valeurs de registre ni de valeur entière. + */ + IBM_LOOSE, -/* Fournit l'identifiant unique pour un ensemble d'instructions. */ -itid_t g_arch_instruction_get_unique_id(const GArchInstruction *); + /** + * Dissimulation des références à des éléments externes pouvant varier avec + * entre compilations : adresses de saut ou d'appel, références vers des tables, + * etc. + */ + IBM_LOCAL, + /** + * Dissimulation des déplacements à partir d'une base. + */ + IBM_STRICT, -/** - * La définition de "GArchProcessor", utile aux traitements complémentaires, ne peut - * se faire en incluant le fichier d'en-tête "processor.h", pour cause de références - * circulaires. - * - * On procède donc à une seconde déclaration, en attendant éventuellement mieux. - */ + /** + * Conservation de toutes les valeurs immédiates et dissimulation des registres. + */ + IBM_LARGE, -/* Depuis "processeur.h" : définition générique d'un processeur d'architecture (instance) */ -typedef struct _GArchProcessor GArchProcessor; + IBM_COUNT +} InstructionBytesMask; -/* Complète un désassemblage accompli pour une instruction. */ -typedef void (* instr_hook_fc) (GArchInstruction *, GArchProcessor *, GProcContext *, GExeFormat *); - -/* Complète un désassemblage accompli pour une instruction. */ -void g_arch_instruction_call_hook(GArchInstruction *, InstrProcessHook, GArchProcessor *, GProcContext *, GExeFormat *); -/* Définit la localisation d'une instruction. */ -void g_arch_instruction_set_range(GArchInstruction *, const mrange_t *); +/* Calcule la localisation d'une instruction. */ +void g_arch_instruction_compute_range(GArchInstruction *, GBinaryPortion *, const vmpa2t *, phys_t); /* Fournit la place mémoire d'une instruction. */ -const mrange_t *g_arch_instruction_get_range(const GArchInstruction *); - - - -/* Fournit la localisation d'une instruction. */ -void g_arch_instruction_get_location(const GArchInstruction *, off_t *, off_t *, vmpa_t *) __attribute__ ((deprecated)); +bool g_arch_instruction_get_range(const GArchInstruction *, mrange_t *); +#define AIF_USER_BIT 4 -/* Liste les registres lus et écrits par l'instruction. */ -void g_arch_instruction_get_rw_registers(const GArchInstruction *, GArchRegister ***, size_t *, GArchRegister ***, size_t *) __attribute__ ((deprecated)); +typedef enum _ArchInstructionFlag +{ + AIF_NONE = (0 << 0), /* Aucune information */ + AIF_ROUTINE_START = (1 << 0), /* Début de routine */ + AIF_RETURN_POINT = (1 << 1), /* Retour de fonction appelée */ + AIF_COND_RETURN_POINT = (1 << 2), /* Retour éventuel de fonction */ + AIF_CALL = (1 << 3), /* Instruction d'appel */ + AIF_LOW_USER = (1 << AIF_USER_BIT), /* Premier bit disponible */ + AIF_HIGH_USER = (1 << 7), /* Dernier bit disponible */ +} ArchInstructionFlag; -/* --------------------------- MANIPULATION DES OPERANDES --------------------------- */ +#define AIF_USER_FLAG(n) (1 << (AIF_USER_BIT + n)) -/* Verrouille les accès à la liste des opérandes. */ -void g_arch_instruction_lock_operands(GArchInstruction *); +/* Ajoute une information complémentaire à une instruction. */ +bool g_arch_instruction_set_flag(GArchInstruction *, ArchInstructionFlag); -/* Déverrouille les accès à la liste des opérandes. */ -void g_arch_instruction_unlock_operands(GArchInstruction *); +/* Retire une information complémentaire à une instruction. */ +bool g_arch_instruction_unset_flag(GArchInstruction *, ArchInstructionFlag); -/* Attache un opérande supplémentaire à une instruction. */ -void g_arch_instruction_attach_extra_operand(GArchInstruction *, GArchOperand *); +/* Détermine si une instruction possède un fanion particulier. */ +bool g_arch_instruction_has_flag(const GArchInstruction *, ArchInstructionFlag); -/* Indique la quantité d'opérandes présents dans l'instruction. */ -size_t _g_arch_instruction_count_operands(const GArchInstruction *); +/* Fournit les particularités de l'instruction. */ +ArchInstructionFlag g_arch_instruction_get_flags(const GArchInstruction *); -/* Fournit un opérande donné d'une instruction. */ -GArchOperand *_g_arch_instruction_get_operand(const GArchInstruction *, size_t); -/* Remplace un opérande d'une instruction par un autre. */ -bool _g_arch_instruction_replace_operand(GArchInstruction *, GArchOperand *, GArchOperand *); +/* Types de crochet de traitement */ +typedef enum _InstrProcessHook +{ + IPH_FETCH, /* Itinéraire de désassemblage */ + IPH_LINK, /* Edition des liens */ + IPH_POST, /* Résolution des symboles */ -/* Détache un opérande liée d'une instruction. */ -bool _g_arch_instruction_detach_operand(GArchInstruction *, GArchOperand *); - - -#define g_arch_instruction_count_operands(ins) \ - ({ \ - size_t __result; \ - g_arch_instruction_lock_operands(ins); \ - __result = _g_arch_instruction_count_operands(ins); \ - g_arch_instruction_unlock_operands(ins); \ - __result; \ - }) - -#define g_arch_instruction_get_operand(ins, idx) \ - ({ \ - GArchOperand *__result; \ - g_arch_instruction_lock_operands(ins); \ - __result = _g_arch_instruction_get_operand(ins, idx); \ - g_arch_instruction_unlock_operands(ins); \ - __result; \ - }) - -#define g_arch_instruction_replace_operand(ins, o, n) \ - ({ \ - bool __result; \ - g_arch_instruction_lock_operands(ins); \ - __result = _g_arch_instruction_replace_operand(ins, o, n); \ - g_arch_instruction_unlock_operands(ins); \ - __result; \ - }) - -#define g_arch_instruction_detach_operand(ins, o) \ - ({ \ - bool __result; \ - g_arch_instruction_lock_operands(ins); \ - __result = _g_arch_instruction_detach_operand(ins, o); \ - g_arch_instruction_unlock_operands(ins); \ - __result; \ - }) + IPH_COUNT +} InstrProcessHook; -/* Détermine le chemin conduisant à un opérande. */ -char *g_arch_instruction_find_operand_path(GArchInstruction *, const GArchOperand *); -/* Obtient l'opérande correspondant à un chemin donné. */ -GArchOperand *g_arch_instruction_get_operand_from_path(GArchInstruction *, const char *); @@ -236,30 +160,24 @@ typedef enum _InstructionLinkType } InstructionLinkType; -/* Déscription d'une liaison entre deux instructions */ -typedef struct _instr_link_t -{ - GArchInstruction *linked; /* Autre instruction liée */ - InstructionLinkType type; /* Type de liaison */ - -} instr_link_t; - -#define ref_instr_link(l) g_object_ref(G_OBJECT(l->linked)); -#define unref_instr_link(l) g_object_unref(G_OBJECT(l->linked)); +/* Détermine si un type de lien amène à une instruction. */ +bool g_arch_instruction_has_src_link(const GArchInstruction *, InstructionLinkType); +/* Détermine si un type de lien émerge d'une instruction. */ +bool g_arch_instruction_has_dest_link(const GArchInstruction *, InstructionLinkType); -/* Met à disposition un encadrement des accès aux liens. */ -void g_arch_instruction_lock_unlock_links(GArchInstruction *, bool, bool); +/* Détermine si une instruction est source d'une autre. */ +bool g_arch_instruction_has_src_link_with(const GArchInstruction *, const GArchInstruction *); -/* Détermine si un type de lien existe dans une instruction. */ -bool g_arch_instruction_has_link(GArchInstruction *, InstructionLinkType); - -/* Détermine si un lien est déjà établi entre deux instructions. */ -bool g_arch_instruction_has_link_to(GArchInstruction *, const GArchInstruction *); +/* Détermine si une instruction est destination d'une autre. */ +bool g_arch_instruction_has_dest_link_with(const GArchInstruction *, const GArchInstruction *); /* Etablit un lien entre deux instructions. */ -void g_arch_instruction_link_with(GArchInstruction *, GArchInstruction *, InstructionLinkType); +void g_arch_instruction_link(GArchInstruction *, GArchInstruction *, InstructionLinkType); + +/* Supprime un lien entre deux instructions. */ +void g_arch_instruction_unlink(GArchInstruction *, GArchInstruction *, InstructionLinkType); /* Change la nature d'un lien entre deux instructions. */ bool g_arch_instruction_change_link(GArchInstruction *, GArchInstruction *, InstructionLinkType, InstructionLinkType); @@ -267,46 +185,72 @@ bool g_arch_instruction_change_link(GArchInstruction *, GArchInstruction *, Inst /* Supprime tous les liens établis avec d'autres instructions. */ void g_arch_instruction_delete_all_links(GArchInstruction *); -#define g_arch_instruction_lock_src(ins) g_arch_instruction_lock_unlock_links(ins, true, true) -#define g_arch_instruction_unlock_src(ins) g_arch_instruction_lock_unlock_links(ins, true, false) +/* Fournit la quantité d'instructions placées en source. */ +size_t g_arch_instruction_count_src_links(const GArchInstruction *); -/* Fournit la quantité d'instructions pointant vers une autre. */ -size_t g_arch_instruction_count_sources(const GArchInstruction *); +/* Fournit la quantité d'instructions placées en destination. */ +size_t g_arch_instruction_count_dest_links(const GArchInstruction *); -/* Fournit les détails d'une origine d'une instruction donnée. */ -const instr_link_t *g_arch_instruction_get_source(GArchInstruction *, size_t); +/* Fournit les détails d'une source donnée d'une instruction. */ +GArchInstruction *g_arch_instruction_get_linked_source(const GArchInstruction *, size_t, InstructionLinkType *); -/* Fournit tous les détails d'origine d'une instruction donnée. */ -instr_link_t *g_arch_instruction_get_sources(GArchInstruction *, size_t *); +/* Fournit les détails d'une destination donnée d'une instruction. */ +GArchInstruction *g_arch_instruction_get_linked_destination(const GArchInstruction *, size_t, InstructionLinkType *); -#define g_arch_instruction_lock_dest(ins) g_arch_instruction_lock_unlock_links(ins, false, true) -#define g_arch_instruction_unlock_dest(ins) g_arch_instruction_lock_unlock_links(ins, false, false) -/* Donne le nombre d'instructions non naturellement suivantes. */ -size_t g_arch_instruction_count_destinations(const GArchInstruction *); -/* Fournit les détails d'une destination d'une instruction. */ -const instr_link_t *g_arch_instruction_get_destination(GArchInstruction *, size_t); +/* --------------------------- MANIPULATION DES OPERANDES --------------------------- */ -/* Fournit la destination d'une instruction et d'un type donné. */ -GArchInstruction *g_arch_instruction_get_given_destination(GArchInstruction *, InstructionLinkType); -/* Fournit tous les détails de destination d'une instruction. */ -instr_link_t *g_arch_instruction_get_destinations(GArchInstruction *, size_t *); +/* Indique la quantité d'opérandes présents dans l'instruction. */ +size_t g_arch_instruction_count_operands(const GArchInstruction *); +/* Attache un opérande supplémentaire à une instruction. */ +void g_arch_instruction_attach_operand(GArchInstruction *, GArchOperand *); +/* Remplace un opérande d'une instruction par un autre. */ +bool g_arch_instruction_replace_operand(GArchInstruction *, GArchOperand *, GArchOperand *); -/* --------------------- CONVERSIONS DU FORMAT DES INSTRUCTIONS --------------------- */ +/* Détache un opérande liée d'une instruction. */ +bool g_arch_instruction_detach_operand(GArchInstruction *, GArchOperand *); +/* Fournit un opérande donné d'une instruction. */ +GArchOperand *g_arch_instruction_get_operand(const GArchInstruction *, size_t); + +/* Détermine le chemin conduisant à un opérande. */ +char *g_arch_instruction_find_operand_path(GArchInstruction *, const GArchOperand *); + +/* Obtient l'opérande correspondant à un chemin donné. */ +GArchOperand *g_arch_instruction_get_operand_from_path(GArchInstruction *, const char *); + + + + +#if 0 + + + +/** + * La définition de "GArchProcessor", utile aux traitements complémentaires, ne peut + * se faire en incluant le fichier d'en-tête "processor.h", pour cause de références + * circulaires. + * + * On procède donc à une seconde déclaration, en attendant éventuellement mieux. + */ + +/* Depuis "processeur.h" : définition générique d'un processeur d'architecture (instance) */ +typedef struct _GArchProcessor GArchProcessor; + + +/* Complète un désassemblage accompli pour une instruction. */ +typedef void (* instr_hook_fc) (GArchInstruction *, GArchProcessor *, GProcContext *, GExeFormat *); + +/* Complète un désassemblage accompli pour une instruction. */ +void g_arch_instruction_call_hook(GArchInstruction *, InstrProcessHook, GArchProcessor *, GProcContext *, GExeFormat *); -/* Fournit le nom humain de l'instruction manipulée. */ -const char *g_arch_instruction_get_keyword(GArchInstruction *); -/* Construit un petit résumé concis de l'instruction. */ -char *g_arch_instruction_build_tooltip(const GArchInstruction *); -/* Fournit une description pour l'instruction manipulée. */ -const char *g_arch_instruction_get_description(const GArchInstruction *); +#endif diff --git a/src/arch/instructions/Makefile.am b/src/arch/instructions/Makefile.am index 28cf90f..d6fc4bd 100644 --- a/src/arch/instructions/Makefile.am +++ b/src/arch/instructions/Makefile.am @@ -1,12 +1,19 @@ -noinst_LTLIBRARIES = libarchinstructions.la +noinst_LTLIBRARIES = libarchinstructions.la libarchinstructionsui.la libarchinstructions_la_SOURCES = \ + raw-int.h \ raw.h raw.c \ undefined-int.h \ undefined.h undefined.c -libarchinstructions_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) +libarchinstructions_la_CFLAGS = $(LIBGOBJ_CFLAGS) + +libarchinstructionsui_la_SOURCES = \ + raw-ui.h raw-ui.c \ + undefined-ui.h undefined-ui.c + +libarchinstructionsui_la_CFLAGS = $(LIBGTK4_CFLAGS) devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) diff --git a/src/arch/instructions/raw-int.h b/src/arch/instructions/raw-int.h new file mode 100644 index 0000000..4a5e64b --- /dev/null +++ b/src/arch/instructions/raw-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * raw-int.h - prototypes pour la définition interne des instructions de données brutes + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_INSTRUCTIONS_RAW_INT_H +#define _ARCH_INSTRUCTIONS_RAW_INT_H + + +#include "raw.h" +#include "../instruction-int.h" + + + +/* Définition générique d'une instruction brute d'architecture (instance) */ +struct _GRawInstruction +{ + GArchInstruction parent; /* A laisser en premier */ + +}; + +/* Définition générique d'une instruction brute d'architecture (instance) */ +struct _GRawInstructionClass +{ + GArchInstructionClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une instruction de type 'db/dw/etc' simple. */ +bool g_raw_instruction_create_value(GRawInstruction *, GBinaryPortion *, const vmpa2t *, MemoryDataSize, uint64_t); + +/* Met en place une instruction de type 'db/dw/etc' étendue. */ +bool g_raw_instruction_create_array(GRawInstruction *, GBinaryPortion *, vmpa2t *, MemoryDataSize, const GBinContent *, size_t, SourceEndian); + + + +#endif /* _ARCH_INSTRUCTIONS_RAW_INT_H */ diff --git a/src/arch/instructions/raw-ui.c b/src/arch/instructions/raw-ui.c new file mode 100644 index 0000000..1026dfb --- /dev/null +++ b/src/arch/instructions/raw-ui.c @@ -0,0 +1,261 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * raw-ui.c - opérandes représentant des instructions de données brutes sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "raw-ui.h" + + +#include <assert.h> +#include <ctype.h> +#include <malloc.h> + + +#include "raw.h" +#include "../operand-ui.h" +#include "../operands/immediate.h" +#include "../../glibext/objhole.h" +#include "../../glibext/options/asm.h" + + + +/* Etablit dans une ligne de rendu le contenu représenté. */ +static void g_raw_instruction_ui_populate_line(const GTokenGenerator *, size_t, size_t, GBufferLine *, void *); + + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de génération. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_raw_instruction_ui_token_generator_iface_init(GTokenGeneratorInterface *iface) +{ + iface->populate = g_raw_instruction_ui_populate_line; + +} + + +/****************************************************************************** +* * +* Paramètres : generator = générateur à utiliser pour l'impression. * +* index = indice de cette même ligne dans le tampon global.* +* repeat = indice d'utilisations successives du générateur. * +* line = ligne de rendu à compléter. * +* data = éventuelle donnée complémentaire fournie. * +* * +* Description : Etablit dans une ligne de rendu le contenu représenté. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_raw_instruction_ui_populate_line(const GTokenGenerator *generator, size_t index, size_t repeat, GBufferLine *line, void *data) +{ + GArchInstruction *instr; /* Version spécialisée #1 */ + GRawInstruction *raw; /* Version spécialisée #2 */ + GBinContent *content; /* Contenu brut d'origine */ + mrange_t range; /* Emplacement couvert */ + phys_t max_displayed_len; /* Quantité de code affichée */ + char *key; /* Mot clef principal */ + char *string; /* Chaîne reconstituée */ + size_t iter; /* Tête d'écriture */ + bool first; /* Mémorise une énumération */ + size_t count; /* Nombre d'opérandes en place */ + size_t i; /* Boucle de parcours */ + GArchOperand *op; /* Opérande à manipuler */ + GImmediateOperand *imm; /* Version opérande de valeur */ + char byte; /* Octet à afficher (ou pas) */ +#ifndef NDEBUG + bool status; /* Bilan d'une récupération */ +#endif + + instr = G_ARCH_INSTRUCTION(generator); + raw = G_RAW_INSTRUCTION(instr); + content = G_BIN_CONTENT(data); + + g_thick_object_lock(G_THICK_OBJECT(instr)); + + /* Prologue */ + + if (g_arch_instruction_get_range(instr, &range)) + { + /* Localisation */ + + g_buffer_line_fill_physical(line, ACO_PHYSICAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&range)); + + g_buffer_line_fill_virtual(line, ACO_VIRTUAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&range)); + + /* Contenu */ + + if (g_raw_instruction_is_padding(raw)) + max_displayed_len = 0; + + else if (g_raw_instruction_is_string(raw)) + max_displayed_len = 1; + + else + { + max_displayed_len = get_mrange_length(&range); + max_displayed_len /= g_arch_instruction_count_operands(instr); + } + + g_buffer_line_fill_content(line, ACO_BINARY, content, &range, max_displayed_len); + + } + + /* Instruction proprement dite */ + + key = g_arch_instruction_get_keyword(instr); + + g_buffer_line_append_text(line, ACO_ASSEMBLY_HEAD, TRT_INSTRUCTION, SL(key), NULL, G_OBJECT(instr)); + + free(key); + + /* Contenu sous forme d'opérandes */ + + if (g_raw_instruction_is_padding(raw)) + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_CHR_PRINTABLE, STCSL("..."), NULL, NULL); + + else + { + string = NULL; + iter = 0; + + first = true; + + count = g_arch_instruction_count_operands(instr); + + for (i = 0; i < count; i++) + { + op = g_arch_instruction_get_operand(instr, i); + + if (!G_IS_IMMEDIATE_OPERAND(op)) + goto fallback; + + imm = G_IMMEDIATE_OPERAND(op); + + if (g_immediate_operand_get_size(imm) != MDS_8_BITS) + goto fallback; + + if (!g_raw_instruction_is_string(raw) && g_immediate_operand_get_display(imm) != IOD_CHAR) + goto fallback; + +#ifndef NDEBUG + status = g_immediate_operand_get_value(imm, MDS_8_BITS, &byte); + assert(status); +#else + g_immediate_operand_get_value(imm, MDS_8_BITS, &byte); +#endif + + /* Si le caractère doit apparaître en hexadécimal... */ + + if (!isprint(byte)) + goto fallback; + + /* Impression de l'octet */ + + if (string == NULL) + { + string = calloc(count + 3, sizeof(char)); + + strcpy(string, "\""); + iter = 1; + + } + + string[iter++] = byte; + + unref_object(op); + + continue; + + fallback: + + /* Si une chaîne précède */ + + if (string != NULL && iter > 1) + { + if (!first) + { + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_PUNCT, STCSL(","), NULL, NULL); + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_CHR_PRINTABLE, STCSL(" "), NULL, NULL); + } + else + first = false; + + string[iter++] = '"'; + + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_STRING, string, iter, NULL, NULL); + + iter = 1; + + } + + /* Intégration en tant qu'opérande classique */ + + if (!first) + { + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_PUNCT, STCSL(","), NULL, NULL); + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_CHR_PRINTABLE, STCSL(" "), NULL, NULL); + } + else + first = false; + + g_arch_operand_ui_print(G_ARCH_OPERAND_UI(op), line); + + unref_object(op); + + } + + /* Si au final une chaîne traine encore */ + + if (string != NULL && iter > 1) + { + if (!first) + { + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_PUNCT, STCSL(","), NULL, NULL); + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_CHR_PRINTABLE, STCSL(" "), NULL, NULL); + } + + string[iter++] = '"'; + + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_STRING, string, iter, NULL, NULL); + + } + + if (string != NULL) + free(string); + + } + + g_thick_object_unlock(G_THICK_OBJECT(instr)); + +} diff --git a/src/arch/instructions/raw-ui.h b/src/arch/instructions/raw-ui.h new file mode 100644 index 0000000..cd604e6 --- /dev/null +++ b/src/arch/instructions/raw-ui.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * raw-ui.h - prototypes pour les opérandes représentant des instructions de données brutes sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_OPERANDS_RAW_UI_H +#define _ARCH_OPERANDS_RAW_UI_H + + +#include "../../glibext/generator-int.h" + + + +/* Procède à l'initialisation de l'interface de génération. */ +void g_raw_instruction_ui_token_generator_iface_init(GTokenGeneratorInterface *); + + + +#endif /* _ARCH_OPERANDS_RAW_UI_H */ diff --git a/src/arch/instructions/raw.c b/src/arch/instructions/raw.c index 26282fa..87297f1 100644 --- a/src/arch/instructions/raw.c +++ b/src/arch/instructions/raw.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * raw.c - instructions pures vues de l'esprit * - * Copyright (C) 2014-2020 Cyrille Bagard + * Copyright (C) 2014-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,36 +25,19 @@ #include <assert.h> -#include <ctype.h> #include <string.h> #include <i18n.h> -#include "../instruction-int.h" +#include "raw-int.h" #include "../operands/immediate.h" -#include "../operands/target.h" -#include "../../core/columns.h" +//#include "../operands/target.h" // FIXME -/* ------------------------- INSTRUCTION INCONNUE / DONNEES ------------------------- */ - - -/* Définition générique d'une instruction brute d'architecture (instance) */ -struct _GRawInstruction -{ - GArchInstruction parent; /* A laisser en premier */ - -}; - -/* Définition générique d'une instruction brute d'architecture (classe) */ -struct _GRawInstructionClass -{ - GArchInstructionClass parent; /* A laisser en premier */ - -}; +/* --------------------- INSTRUCTION AVEC JEU DE DONNEES BRUTES --------------------- */ /* Initialise la classe des instructions brutes d'architecture. */ @@ -64,40 +47,26 @@ static void g_raw_instruction_class_init(GRawInstructionClass *); static void g_raw_instruction_init(GRawInstruction *); /* Supprime toutes les références externes. */ -static void g_raw_instruction_dispose(GRawInstruction *); +static void g_raw_instruction_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_raw_instruction_finalize(GRawInstruction *); - -/* Indique l'encodage d'une instruction de façon détaillée. */ -static const char *g_raw_instruction_get_encoding(const GRawInstruction *); - -/* Fournit le nom humain de l'instruction manipulée. */ -static const char *g_raw_instruction_get_keyword(const GRawInstruction *); +static void g_raw_instruction_finalize(GObject *); -/* -------------------- CONSERVATION SUR DISQUE DES INSTRUCTIONS -------------------- */ +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ -/* Charge une instruction depuis une mémoire tampon. */ -static bool g_raw_instruction_unserialize(GRawInstruction *, GAsmStorage *, GBinFormat *, packed_buffer_t *); - -/* Sauvegarde une instruction dans une mémoire tampon. */ -static bool g_raw_instruction_serialize(GRawInstruction *, GAsmStorage *, packed_buffer_t *); - - - -/* ------------------------ OFFRE DE CAPACITES DE GENERATION ------------------------ */ - +/* Indique l'encodage d'une instruction de façon détaillée. */ +static char *g_raw_instruction_get_encoding(const GArchInstruction *); -/* Ajoute à un tampon GLib le contenu de l'instance spécifiée. */ -static void g_raw_instruction_print(GRawInstruction *, GBufferLine *, size_t, size_t, const GBinContent *); +/* Fournit le nom humain de l'instruction manipulée. */ +static char *g_raw_instruction_get_keyword(const GArchInstruction *); /* ---------------------------------------------------------------------------------- */ -/* INSTRUCTION INCONNUE / DONNEES */ +/* INSTRUCTION AVEC JEU DE DONNEES BRUTES */ /* ---------------------------------------------------------------------------------- */ @@ -124,18 +93,13 @@ static void g_raw_instruction_class_init(GRawInstructionClass *klass) object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_raw_instruction_dispose; - object->finalize = (GObjectFinalizeFunc)g_raw_instruction_finalize; + object->dispose = g_raw_instruction_dispose; + object->finalize = g_raw_instruction_finalize; instr = G_ARCH_INSTRUCTION_CLASS(klass); - instr->get_encoding = (get_instruction_encoding_fc)g_raw_instruction_get_encoding; - instr->get_keyword = (get_instruction_keyword_fc)g_raw_instruction_get_keyword; - - instr->unserialize = (unserialize_instruction_fc)g_raw_instruction_unserialize; - instr->serialize = (serialize_instruction_fc)g_raw_instruction_serialize; - - instr->print = (print_instruction_fc)g_raw_instruction_print; + instr->get_encoding = g_raw_instruction_get_encoding; + instr->get_keyword = g_raw_instruction_get_keyword; } @@ -160,7 +124,7 @@ static void g_raw_instruction_init(GRawInstruction *instr) /****************************************************************************** * * -* Paramètres : instr = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -170,16 +134,16 @@ static void g_raw_instruction_init(GRawInstruction *instr) * * ******************************************************************************/ -static void g_raw_instruction_dispose(GRawInstruction *instr) +static void g_raw_instruction_dispose(GObject *object) { - G_OBJECT_CLASS(g_raw_instruction_parent_class)->dispose(G_OBJECT(instr)); + G_OBJECT_CLASS(g_raw_instruction_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : instr = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -189,16 +153,17 @@ static void g_raw_instruction_dispose(GRawInstruction *instr) * * ******************************************************************************/ -static void g_raw_instruction_finalize(GRawInstruction *instr) +static void g_raw_instruction_finalize(GObject *object) { - G_OBJECT_CLASS(g_raw_instruction_parent_class)->finalize(G_OBJECT(instr)); + G_OBJECT_CLASS(g_raw_instruction_parent_class)->finalize(object); } /****************************************************************************** * * -* Paramètres : addr = position à associer à l'instruction. * +* Paramètres : area = portion de binaire incluant l'instruction. * +* addr = adresse virtuelle et/ou position physique. * * size = taille de l'opérande souhaitée. * * value = valeur sur x bits à venir récupérer. * * * @@ -210,127 +175,106 @@ static void g_raw_instruction_finalize(GRawInstruction *instr) * * ******************************************************************************/ -GArchInstruction *g_raw_instruction_new_from_value(const vmpa2t *addr, MemoryDataSize size, uint64_t value) +GArchInstruction *g_raw_instruction_new_from_value(GBinaryPortion *area, const vmpa2t *addr, MemoryDataSize size, uint64_t value) { GArchInstruction *result; /* Instruction à retourner */ - GArchOperand *operand; /* Octet non décodé à afficher */ - mrange_t range; /* Couverture de l'instruction */ result = g_object_new(G_TYPE_RAW_INSTRUCTION, NULL); - operand = g_imm_operand_new_from_value(size, value); - if (operand == NULL) goto error; + if (!g_raw_instruction_create_value(G_RAW_INSTRUCTION(result), area, addr, size, value)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instance à initialiser pleinement. * +* area = portion de binaire incluant l'instruction. * +* addr = adresse virtuelle et/ou position physique. * +* size = taille de chacun des éléments à représenter. * +* value = valeur sur x bits à venir récupérer. * +* * +* Description : Met en place une instruction de type 'db/dw/etc' simple. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_raw_instruction_create_value(GRawInstruction *instr, GBinaryPortion *area, const vmpa2t *addr, MemoryDataSize size, uint64_t value) +{ + bool result; /* Bilan à retourner */ + GArchOperand *operand; /* Octet non décodé à afficher */ + uint16_t length; /* Taille de l'instruction */ + + result = false; + + operand = g_immediate_operand_new_from_value(size, value); + if (operand == NULL) goto exit; g_arch_operand_set_flag(operand, IOF_ZERO_PADDING_BY_DEFAULT | IOF_ZERO_PADDING); - g_arch_instruction_attach_extra_operand(result, operand); + g_thick_object_lock(G_THICK_OBJECT(instr)); + + g_arch_instruction_attach_operand(G_ARCH_INSTRUCTION(instr), operand); + unref_object(operand); + + g_thick_object_unlock(G_THICK_OBJECT(instr)); switch (size) { case MDS_8_BITS_UNSIGNED: case MDS_8_BITS_SIGNED: - init_mrange(&range, addr, 1); + length = 1; break; case MDS_16_BITS_UNSIGNED: case MDS_16_BITS_SIGNED: - init_mrange(&range, addr, 2); + length = 2; break; case MDS_32_BITS_UNSIGNED: case MDS_32_BITS_SIGNED: - init_mrange(&range, addr, 4); + length = 4; break; case MDS_64_BITS_UNSIGNED: case MDS_64_BITS_SIGNED: - init_mrange(&range, addr, 8); + length = 8; break; default: assert(false); - goto error; + goto exit; break; } - g_arch_instruction_set_range(result, &range); + g_arch_instruction_compute_range(G_ARCH_INSTRUCTION(instr), area, addr, length); - return result; - - error: - - g_object_unref(G_OBJECT(result)); - - return NULL; - -} + result = true; - -/****************************************************************************** -* * -* Paramètres : content = flux de données à analyser. * -* addr = position courante dans ce flux. [OUT] * -* * -* Description : Crée une instruction de type 'db/dw/etc' pour un uleb128. * -* * -* Retour : Instruction mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GArchInstruction *g_raw_instruction_new_uleb128(const GBinContent *content, vmpa2t *addr) -{ - GArchInstruction *result; /* Instruction à retourner */ - vmpa2t start; /* Départ original de lecture */ - uleb128_t value; /* Valeur uleb128 à représenter*/ - phys_t diff; /* Couverture de la lecture */ - MemoryDataSize leb_size; /* Taille de la valeur */ - GArchOperand *operand; /* Octet non décodé à afficher */ - mrange_t range; /* Couverture de l'instruction */ - - result = NULL; - - copy_vmpa(&start, addr); - - if (!g_binary_content_read_uleb128(content, addr, &value)) - goto error; - - diff = compute_vmpa_diff(&start, addr); - - leb_size = MDS_FROM_BYTES(diff); - assert(leb_size != MDS_UNDEFINED); - - result = g_object_new(G_TYPE_RAW_INSTRUCTION, NULL); - - init_mrange(&range, &start, diff); - g_arch_instruction_set_range(result, &range); - - operand = g_imm_operand_new_from_value(leb_size, (uint64_t)value); - if (operand == NULL) goto error; - - g_arch_operand_set_flag(operand, IOF_ZERO_PADDING_BY_DEFAULT | IOF_ZERO_PADDING); - - g_arch_instruction_attach_extra_operand(result, operand); + exit: return result; - error: - - g_clear_object(&result); - - return NULL; - } /****************************************************************************** * * -* Paramètres : content = flux de données à analyser. * -* addr = position courante dans ce flux. [OUT] * +* Paramètres : area = portion de binaire incluant l'instruction. * +* addr = adresse virtuelle et/ou position physique. * +* size = taille de chacun des éléments à représenter. * +* content = flux de données à analyser. * +* count = nombre de ces éléments. * +* endian = ordre des bits dans la source. * * * -* Description : Crée une instruction de type 'db/dw/etc' pour un sleb128. * +* Description : Crée une instruction de type 'db/dw/etc' étendue. * * * * Retour : Instruction mise en place. * * * @@ -338,256 +282,174 @@ GArchInstruction *g_raw_instruction_new_uleb128(const GBinContent *content, vmpa * * ******************************************************************************/ -GArchInstruction *g_raw_instruction_new_sleb128(const GBinContent *content, vmpa2t *addr) +GArchInstruction *g_raw_instruction_new_array(GBinaryPortion *area, vmpa2t *addr, MemoryDataSize size, const GBinContent *content, size_t count, SourceEndian endian) { GArchInstruction *result; /* Instruction à retourner */ - vmpa2t start; /* Départ original de lecture */ - uleb128_t value; /* Valeur uleb128 à représenter*/ - phys_t diff; /* Couverture de la lecture */ - MemoryDataSize leb_size; /* Taille de la valeur */ - GArchOperand *operand; /* Octet non décodé à afficher */ - mrange_t range; /* Couverture de l'instruction */ - - result = NULL; - - copy_vmpa(&start, addr); - - if (!g_binary_content_read_uleb128(content, addr, &value)) - goto error; - - diff = compute_vmpa_diff(&start, addr); - - leb_size = MDS_FROM_BYTES(diff) | MDS_SIGN; - assert(leb_size != MDS_SIGN); result = g_object_new(G_TYPE_RAW_INSTRUCTION, NULL); - init_mrange(&range, &start, diff); - g_arch_instruction_set_range(result, &range); - - operand = g_imm_operand_new_from_value(leb_size, (uint64_t)value); - if (operand == NULL) goto error; - - g_arch_operand_set_flag(operand, IOF_ZERO_PADDING_BY_DEFAULT | IOF_ZERO_PADDING); - - g_arch_instruction_attach_extra_operand(result, operand); + if (!g_raw_instruction_create_array(G_RAW_INSTRUCTION(result), area, addr, size, content, count, endian)) + g_clear_object(&result); return result; - error: - - g_clear_object(&result); - - return NULL; - } /****************************************************************************** * * -* Paramètres : content = flux de données à analyser. * +* Paramètres : instr = instance à initialiser pleinement. * +* area = portion de binaire incluant l'instruction. * +* addr = adresse virtuelle et/ou position physique. * * size = taille de chacun des éléments à représenter. * +* content = flux de données à analyser. * * count = nombre de ces éléments. * -* addr = position courante dans ce flux. [OUT] * * endian = ordre des bits dans la source. * * * -* Description : Crée une instruction de type 'db/dw/etc' étendue. * +* Description : Met en place une instruction de type 'db/dw/etc' étendue. * * * -* Retour : Instruction mise en place. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -GArchInstruction *g_raw_instruction_new_array(const GBinContent *content, MemoryDataSize size, size_t count, vmpa2t *addr, SourceEndian endian) +bool g_raw_instruction_create_array(GRawInstruction *instr, GBinaryPortion *area, vmpa2t *addr, MemoryDataSize size, const GBinContent *content, size_t count, SourceEndian endian) { - GArchInstruction *result; /* Instruction à retourner */ - vmpa2t old; /* Sauvegarde de la position */ + bool result; /* Bilan à retourner */ + vmpa2t start; /* Sauvegarde de la position */ size_t i; /* Boucle de parcours */ GArchOperand *operand; /* Octet non décodé à afficher */ - mrange_t range; /* Couverture de l'instruction */ + phys_t diff; /* Décalage à appliquer */ + + result = false; /* Par soucis de cohérence */ - if (count == 0) return NULL; + if (count == 0) + goto exit; - result = g_object_new(G_TYPE_RAW_INSTRUCTION, NULL); + copy_vmpa(&start, addr); - copy_vmpa(&old, addr); + g_thick_object_lock(G_THICK_OBJECT(instr)); for (i = 0; i < count; i++) { - operand = g_imm_operand_new_from_data(size, content, addr, endian); - if (operand == NULL) goto error; + operand = g_immediate_operand_new_from_data(size, content, addr, (bool []){ false /* unused */ }, endian); + if (operand == NULL) break; g_arch_operand_set_flag(operand, IOF_ZERO_PADDING_BY_DEFAULT | IOF_ZERO_PADDING); - g_arch_instruction_attach_extra_operand(result, operand); + g_arch_instruction_attach_operand(G_ARCH_INSTRUCTION(instr), operand); + unref_object(operand); } - init_mrange(&range, &old, compute_vmpa_diff(addr, &old)); + g_thick_object_unlock(G_THICK_OBJECT(instr)); - g_arch_instruction_set_range(result, &range); + if (i < count) + goto exit; - return result; + diff = compute_vmpa_diff(addr, &start); + + g_arch_instruction_compute_range(G_ARCH_INSTRUCTION(instr), area, &start, diff); - error: + result = true; - g_object_unref(G_OBJECT(result)); + exit: - return NULL; + return result; } /****************************************************************************** * * -* Paramètres : instr = instruction quelconque à consulter. * +* Paramètres : instr = instruction à traiter. * +* is_padding = nouveau statut à associer au contenu. * * * -* Description : Indique l'encodage d'une instruction de façon détaillée. * +* Description : Marque l'instruction comme ne contenant que du bourrage. * * * -* Retour : Description humaine de l'encodage utilisé. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static const char *g_raw_instruction_get_encoding(const GRawInstruction *instr) +void g_raw_instruction_mark_as_padding(GRawInstruction *instr, bool is_padding) { - const char *result; /* Description à retourner */ - - if (g_raw_instruction_is_string(instr)) - result = _("String"); + if (is_padding) + g_arch_instruction_set_flag(G_ARCH_INSTRUCTION(instr), RIF_PADDING); else - result = _("Raw"); - - return result; + g_arch_instruction_unset_flag(G_ARCH_INSTRUCTION(instr), RIF_PADDING); } /****************************************************************************** * * -* Paramètres : instr = instruction d'assemblage à consulter. * +* Paramètres : instr = instruction à traiter. * +* is_padding = nouveau statut à associer au contenu. * * * -* Description : Fournit le nom humain de l'instruction manipulée. * +* Description : Indique si le contenu de l'instruction est du bourrage. * * * -* Retour : Mot clef de bas niveau. * +* Retour : Statut du contenu de l'instruction. * * * * Remarques : - * * * ******************************************************************************/ -static const char *g_raw_instruction_get_keyword(const GRawInstruction *instr) +bool g_raw_instruction_is_padding(const GRawInstruction *instr) { - GArchOperand *operand; /* Octet décodé à afficher */ - MemoryDataSize size; /* Taille de valeur associée */ - - static char *defines[] = { "dn", "db", "dw", "dd", "dq" }; - - operand = g_arch_instruction_get_operand(G_ARCH_INSTRUCTION(instr), 0); - - if (G_IS_TARGET_OPERAND(operand)) - size = g_target_operand_get_size(G_TARGET_OPERAND(operand)); - else - size = g_imm_operand_get_size(G_IMM_OPERAND(operand)); + bool result; /* Indication à retourner */ - g_object_unref(G_OBJECT(operand)); + result = g_arch_instruction_has_flag(G_ARCH_INSTRUCTION(instr), RIF_PADDING); - return defines[MDS_RANGE(size)]; + return result; } - -/* ---------------------------------------------------------------------------------- */ -/* CONSERVATION SUR DISQUE DES INSTRUCTIONS */ -/* ---------------------------------------------------------------------------------- */ - - /****************************************************************************** * * -* Paramètres : instr = instruction d'assemblage à consulter. * -* storage = mécanisme de sauvegarde à manipuler. * -* format = format binaire chargé associé à l'architecture. * -* pbuf = zone tampon à remplir. * +* Paramètres : instr = instruction à traiter. * +* is_string = nouveau statut à associer au contenu. * * * -* Description : Charge une instruction depuis une mémoire tampon. * +* Description : Marque l'instruction comme contenant une chaîne de texte. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_raw_instruction_unserialize(GRawInstruction *instr, GAsmStorage *storage, GBinFormat *format, packed_buffer_t *pbuf) +void g_raw_instruction_mark_as_string(GRawInstruction *instr, bool is_string) { - bool result; /* Bilan à retourner */ - GArchInstructionClass *parent; /* Classe parente à consulter */ - uint8_t boolean; /* Valeur booléenne */ - - parent = G_ARCH_INSTRUCTION_CLASS(g_raw_instruction_parent_class); - - result = parent->unserialize(G_ARCH_INSTRUCTION(instr), storage, format, pbuf); - - if (result) - { - result = extract_packed_buffer(pbuf, &boolean, sizeof(uint8_t), false); - - if (result) - g_raw_instruction_mark_as_padding(instr, (boolean == 1)); - - } - - if (result) - { - result = extract_packed_buffer(pbuf, &boolean, sizeof(uint8_t), false); - - if (result) - g_raw_instruction_mark_as_string(instr, (boolean == 1)); - - } - - return result; + if (is_string) + g_arch_instruction_set_flag(G_ARCH_INSTRUCTION(instr), RIF_STRING); + else + g_arch_instruction_unset_flag(G_ARCH_INSTRUCTION(instr), RIF_STRING); } /****************************************************************************** * * -* Paramètres : instr = instruction d'assemblage à consulter. * -* storage = mécanisme de sauvegarde à manipuler. * -* pbuf = zone tampon à remplir. * +* Paramètres : instr = instruction à traiter. * +* is_string = nouveau statut à associer au contenu. * * * -* Description : Sauvegarde une instruction dans une mémoire tampon. * +* Description : Indique si le contenu de l'instruction est un texte. * * * -* Retour : Bilan de l'opération. * +* Retour : Statut du contenu de l'instruction. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_raw_instruction_serialize(GRawInstruction *instr, GAsmStorage *storage, packed_buffer_t *pbuf) +bool g_raw_instruction_is_string(const GRawInstruction *instr) { - bool result; /* Bilan à retourner */ - GArchInstructionClass *parent; /* Classe parente à consulter */ - uint8_t boolean; /* Valeur booléenne */ - - parent = G_ARCH_INSTRUCTION_CLASS(g_raw_instruction_parent_class); - - result = parent->serialize(G_ARCH_INSTRUCTION(instr), storage, pbuf); - - if (result) - { - boolean = (g_raw_instruction_is_padding(instr) ? 1 : 0); - result = extend_packed_buffer(pbuf, &boolean, sizeof(uint8_t), false); - } + bool result; /* Indication à retourner */ - if (result) - { - boolean = (g_raw_instruction_is_string(instr) ? 1 : 0); - result = extend_packed_buffer(pbuf, &boolean, sizeof(uint8_t), false); - } + result = g_arch_instruction_has_flag(G_ARCH_INSTRUCTION(instr), RIF_STRING); return result; @@ -596,285 +458,73 @@ static bool g_raw_instruction_serialize(GRawInstruction *instr, GAsmStorage *sto /* ---------------------------------------------------------------------------------- */ -/* OFFRE DE CAPACITES DE GENERATION */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : instr = instruction d'assemblage à représenter. * -* line = ligne de rendu à compléter. * -* index = indice de cette même ligne dans le tampon global. * -* repeat = indice d'utilisations successives du générateur. * -* content = éventuel contenu binaire brut à imprimer. * +* Paramètres : instr = instruction quelconque à consulter. * * * -* Description : Ajoute à un tampon GLib le contenu de l'instance spécifiée. * +* Description : Indique l'encodage d'une instruction de façon détaillée. * * * -* Retour : - * +* Retour : Description humaine de l'encodage utilisé. * * * * Remarques : - * * * ******************************************************************************/ -static void g_raw_instruction_print(GRawInstruction *instr, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content) +static char *g_raw_instruction_get_encoding(const GArchInstruction *instr) { - GArchInstruction *base; /* Autre version de l'instance */ - phys_t max_displayed_len; /* Quantité de code affichée */ - const char *key; /* Mot clef principal */ - size_t klen; /* Taille de ce mot clef */ - char *string; /* Chaîne reconstituée */ - size_t iter; /* Tête d'écriture */ - bool first; /* Mémorise une énumération */ - size_t count; /* Nombre d'opérandes en place */ - size_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à manipuler */ - GImmOperand *imm; /* Version opérande de valeur */ - char byte; /* Octet à afficher (ou pas) */ -#ifndef NDEBUG - bool status; /* Bilan d'une récupération */ -#endif - - base = G_ARCH_INSTRUCTION(instr); - - /* Localisation */ - - g_buffer_line_fill_phys(line, DLC_PHYSICAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&base->range)); - - g_buffer_line_fill_virt(line, DLC_VIRTUAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&base->range)); - - /* Contenu */ - - if (g_raw_instruction_is_padding(instr)) - max_displayed_len = 0; - - else if (g_raw_instruction_is_string(instr)) - max_displayed_len = 1; - - else - { - max_displayed_len = get_mrange_length(&base->range); - max_displayed_len /= g_arch_instruction_count_operands(base); - } - - g_buffer_line_fill_content(line, DLC_BINARY, content, &base->range, max_displayed_len); - - /* Zone du code d'assemblage */ + char *result; /* Description à retourner */ + GRawInstruction *raw; /* Version spécialisée */ - key = g_arch_instruction_get_keyword(base); - klen = strlen(key); - - g_buffer_line_append_text(line, DLC_ASSEMBLY_HEAD, key, klen, RTT_INSTRUCTION, NULL); - - if (g_raw_instruction_is_padding(instr)) - g_buffer_line_append_text(line, DLC_ASSEMBLY, "...", 3, RTT_RAW, NULL); + raw = G_RAW_INSTRUCTION(instr); + if (g_raw_instruction_is_string(raw)) + result = strdup(_("String")); else - { - string = NULL; - iter = 0; - - first = true; - - g_arch_instruction_lock_operands(base); - - count = _g_arch_instruction_count_operands(base); - - for (i = 0; i < count; i++) - { - op = _g_arch_instruction_get_operand(base, i); - - if (!G_IS_IMM_OPERAND(op)) - goto grip_fallback; - - imm = G_IMM_OPERAND(op); - - if (g_imm_operand_get_size(imm) != MDS_8_BITS) - goto grip_fallback; - - if (!g_raw_instruction_is_string(instr) && g_imm_operand_get_display(imm) != IOD_CHAR) - goto grip_fallback; - -#ifndef NDEBUG - status = g_imm_operand_get_value(imm, MDS_8_BITS, &byte); - assert(status); -#else - g_imm_operand_get_value(imm, MDS_8_BITS, &byte); -#endif - - /* Si le caractère doit apparaître en hexadécimal... */ - - if (!isprint(byte)) - goto grip_fallback; - - /* Impression de l'octet */ - - if (string == NULL) - { - string = (char *)calloc(count + 3, sizeof(char)); - - strcpy(string, "\""); - iter = 1; - - } - - string[iter++] = byte; - - g_object_unref(G_OBJECT(op)); - - continue; - - grip_fallback: - - /* Si une chaîne précède */ - - if (string != NULL && iter > 1) - { - if (!first) - { - g_buffer_line_append_text(line, DLC_ASSEMBLY, ",", 1, RTT_PUNCT, NULL); - g_buffer_line_append_text(line, DLC_ASSEMBLY, " ", 1, RTT_RAW, NULL); - } - else - first = false; - - string[iter++] = '"'; - - g_buffer_line_append_text(line, DLC_ASSEMBLY, string, iter, RTT_STRING, NULL); - - iter = 1; - - } - - /* Intégration en tant qu'opérande classique */ - - if (!first) - { - g_buffer_line_append_text(line, DLC_ASSEMBLY, ",", 1, RTT_PUNCT, NULL); - g_buffer_line_append_text(line, DLC_ASSEMBLY, " ", 1, RTT_RAW, NULL); - } - else - first = false; - - g_arch_operand_print(op, line); - - g_object_unref(G_OBJECT(op)); - - } - - /* Si au final une chaîne traine encore */ - - if (string != NULL && iter > 1) - { - if (!first) - { - g_buffer_line_append_text(line, DLC_ASSEMBLY, ",", 1, RTT_PUNCT, NULL); - g_buffer_line_append_text(line, DLC_ASSEMBLY, " ", 1, RTT_RAW, NULL); - } - - string[iter++] = '"'; - - g_buffer_line_append_text(line, DLC_ASSEMBLY, string, iter, RTT_STRING, NULL); - - } + result = strdup(_("Raw")); - g_arch_instruction_unlock_operands(base); - - if (string != NULL) - free(string); - - } - -} - - -/****************************************************************************** -* * -* Paramètres : instr = instruction à traiter. * -* is_padding = nouveau statut à associer au contenu. * -* * -* Description : Marque l'instruction comme ne contenant que du bourrage. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_raw_instruction_mark_as_padding(GRawInstruction *instr, bool is_padding) -{ - if (is_padding) - g_arch_instruction_set_flag(G_ARCH_INSTRUCTION(instr), RIF_PADDING); - else - g_arch_instruction_unset_flag(G_ARCH_INSTRUCTION(instr), RIF_PADDING); + return result; } /****************************************************************************** * * -* Paramètres : instr = instruction à traiter. * -* is_padding = nouveau statut à associer au contenu. * +* Paramètres : instr = instruction d'assemblage à consulter. * * * -* Description : Indique si le contenu de l'instruction est du bourrage. * +* Description : Fournit le nom humain de l'instruction manipulée. * * * -* Retour : Statut du contenu de l'instruction. * +* Retour : Mot clef de bas niveau. * * * * Remarques : - * * * ******************************************************************************/ -bool g_raw_instruction_is_padding(const GRawInstruction *instr) +static char *g_raw_instruction_get_keyword(const GArchInstruction *instr) { - bool result; /* Indication à retourner */ - - result = g_arch_instruction_has_flag(G_ARCH_INSTRUCTION(instr), RIF_PADDING); - - return result; - -} + char *result; /* Désignation à retourner */ + GArchOperand *operand; /* Octet décodé à afficher */ + MemoryDataSize size; /* Taille de valeur associée */ + static char *defines[] = { "dn", "db", "dw", "dd", "dq" }; -/****************************************************************************** -* * -* Paramètres : instr = instruction à traiter. * -* is_string = nouveau statut à associer au contenu. * -* * -* Description : Marque l'instruction comme contenant une chaîne de texte. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + g_thick_object_lock(G_THICK_OBJECT(instr)); -void g_raw_instruction_mark_as_string(GRawInstruction *instr, bool is_string) -{ - if (is_string) - g_arch_instruction_set_flag(G_ARCH_INSTRUCTION(instr), RIF_STRING); - else - g_arch_instruction_unset_flag(G_ARCH_INSTRUCTION(instr), RIF_STRING); - -} + operand = g_arch_instruction_get_operand(instr, 0); + /*if (G_IS_TARGET_OPERAND(operand)) FIXME + size = g_target_operand_get_size(G_TARGET_OPERAND(operand)); + else*/ + size = g_immediate_operand_get_size(G_IMMEDIATE_OPERAND(operand)); -/****************************************************************************** -* * -* Paramètres : instr = instruction à traiter. * -* is_string = nouveau statut à associer au contenu. * -* * -* Description : Indique si le contenu de l'instruction est un texte. * -* * -* Retour : Statut du contenu de l'instruction. * -* * -* Remarques : - * -* * -******************************************************************************/ + unref_object(operand); -bool g_raw_instruction_is_string(const GRawInstruction *instr) -{ - bool result; /* Indication à retourner */ + g_thick_object_unlock(G_THICK_OBJECT(instr)); - result = g_arch_instruction_has_flag(G_ARCH_INSTRUCTION(instr), RIF_STRING); + result = strdup(defines[MDS_RANGE(size)]); return result; diff --git a/src/arch/instructions/raw.h b/src/arch/instructions/raw.h index 4e92cd4..712f877 100644 --- a/src/arch/instructions/raw.h +++ b/src/arch/instructions/raw.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * raw.h - prototypes pour les instructions pures vues de l'esprit + * raw.h - prototypes pour les instructions de données brutes * - * Copyright (C) 2014-2020 Cyrille Bagard + * Copyright (C) 2014-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,46 +25,24 @@ #define _ARCH_INSTRUCTIONS_RAW_H -#include <glib-object.h> - - #include "../instruction.h" #include "../vmpa.h" +#include "../../analysis/content.h" +#include "../../glibext/helpers.h" -/* ------------------------- INSTRUCTION INCONNUE / DONNEES ------------------------- */ - - -#define G_TYPE_RAW_INSTRUCTION g_raw_instruction_get_type() -#define G_RAW_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RAW_INSTRUCTION, GRawInstruction)) -#define G_IS_RAW_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RAW_INSTRUCTION)) -#define G_RAW_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RAW_INSTRUCTION, GRawInstructionClass)) -#define G_IS_RAW_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RAW_INSTRUCTION)) -#define G_RAW_INSTRUCTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RAW_INSTRUCTION, GRawInstructionClass)) - +#define G_TYPE_RAW_INSTRUCTION (g_raw_instruction_get_type()) -/* Définition générique d'une instruction brute d'architecture (instance) */ -typedef struct _GRawInstruction GRawInstruction; +DECLARE_GTYPE(GRawInstruction, g_raw_instruction, G, RAW_INSTRUCTION); -/* Définition générique d'une instruction brute d'architecture (classe) */ -typedef struct _GRawInstructionClass GRawInstructionClass; - - -/* Indique le type défini pour une instruction inconnue d'architecture. */ -GType g_raw_instruction_get_type(void); /* Crée une instruction de type 'db/dw/etc' simple. */ -GArchInstruction *g_raw_instruction_new_from_value(const vmpa2t *, MemoryDataSize, uint64_t); +GArchInstruction *g_raw_instruction_new_from_value(GBinaryPortion *, const vmpa2t *, MemoryDataSize, uint64_t); -/* Crée une instruction de type 'db/dw/etc' pour un uleb128. */ -GArchInstruction *g_raw_instruction_new_uleb128(const GBinContent *content, vmpa2t *); +/* Crée une instruction de type 'db/dw/etc' étendue. */ +GArchInstruction *g_raw_instruction_new_array(GBinaryPortion *, vmpa2t *, MemoryDataSize, const GBinContent *, size_t, SourceEndian); -/* Crée une instruction de type 'db/dw/etc' pour un sleb128. */ -GArchInstruction *g_raw_instruction_new_sleb128(const GBinContent *content, vmpa2t *); - -/* Crée une instruction de type 'db/dw/etc' étendue. */ -GArchInstruction *g_raw_instruction_new_array(const GBinContent *, MemoryDataSize, size_t, vmpa2t *, SourceEndian); /* Drapeaux pour informations complémentaires */ typedef enum _RawInstrFlag @@ -74,6 +52,7 @@ typedef enum _RawInstrFlag } RawInstrFlag; + /* Marque l'instruction comme ne contenant que du bourrage. */ void g_raw_instruction_mark_as_padding(GRawInstruction *, bool); diff --git a/src/arch/instructions/undefined-int.h b/src/arch/instructions/undefined-int.h index a9b7627..faf0b4b 100644 --- a/src/arch/instructions/undefined-int.h +++ b/src/arch/instructions/undefined-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * undefined-int.h - prototypes pour la définition générique interne des instructions au comportement non défini + * undefined-int.h - prototypes pour la définition interne des instructions au comportement non défini * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -27,42 +27,18 @@ #include "undefined.h" #include "../instruction-int.h" -#include "../../glibext/objhole.h" -/* Informations glissées dans la structure GObject de GArchInstruction */ -typedef struct _undef_extra_data_t -{ - /** - * Le champ uid de la structure parente attendue conduit à une taille - * alignée sur 2 octets, donc à une taille totale de 4 octets ce qui - * représente la limite maximale de taille supportée. - * - * Pour 3 octets à la base, qui devraient laisser 8 - 1 octets disponbibles - * en incluant le bit de verrouillage. - * - * On reproduit donc la structure instr_extra_data_t ici, en basculant - * l'énumération InstrExpectedBehavior en champ de bits. - */ - - itid_t uid; /* Identifiant unique du type */ - ArchInstrFlag flags; /* Informations complémentaires*/ - - unsigned int behavior : 2; /* Conséquences réelles */ - -} undef_extra_data_t; - - /* Définition générique d'une instruction au comportement non défini (instance) */ -struct _GUndefInstruction +struct _GUndefinedInstruction { GArchInstruction parent; /* A laisser en premier */ }; /* Définition générique d'une instruction au comportement non défini (classe) */ -struct _GUndefInstructionClass +struct _GUndefinedInstructionClass { GArchInstructionClass parent; /* A laisser en premier */ @@ -73,15 +49,25 @@ struct _GUndefInstructionClass * Accès aux informations éventuellement déportées. */ -#if 1 //__SIZEOF_INT__ == __SIZEOF_LONG__ +/* Informations glissées dans la structure GObject de GArchInstruction */ +typedef struct _undef_extra_data_t +{ + ARCH_INSTRUCTION_EXTRA_DATA + + unsigned int behavior : 2; /* Conséquences réelles */ + +} undef_extra_data_t; + -# define GET_UNDEF_INSTR_EXTRA(ins) ((undef_extra_data_t *)&((GArchInstruction *)ins)->extra) +#define GET_UNDEF_INSTR_EXTRA(op) \ + GET_GOBJECT_EXTRA(op, undef_extra_data_t) -#else +#define SET_UNDEF_INSTR_EXTRA(op, data) \ + SET_GOBJECT_EXTRA(op, undef_extra_data_t, data) -# define GET_UNDEF_INSTR_EXTRA(ins) GET_GOBJECT_EXTRA(G_OBJECT(ins), undef_extra_data_t) -#endif +/* Met en place une instruction au comportement indéfini. */ +bool g_undefined_instruction_create(GUndefinedInstruction *, InstrExpectedBehavior); diff --git a/src/arch/instructions/undefined-ui.c b/src/arch/instructions/undefined-ui.c new file mode 100644 index 0000000..fbc0452 --- /dev/null +++ b/src/arch/instructions/undefined-ui.c @@ -0,0 +1,102 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * undefined-ui.c - opérandes représentant des instructions indéfinies sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "undefined-ui.h" + + +#include "../instruction.h" +#include "../../glibext/options/asm.h" + + + +/* Etablit dans une ligne de rendu le contenu représenté. */ +static void g_undefined_instruction_ui_populate_line(const GTokenGenerator *, size_t, size_t, GBufferLine *, void *); + + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de génération. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_undefined_instruction_ui_token_generator_iface_init(GTokenGeneratorInterface *iface) +{ + iface->populate = g_undefined_instruction_ui_populate_line; + +} + + +/****************************************************************************** +* * +* Paramètres : generator = générateur à utiliser pour l'impression. * +* index = indice de cette même ligne dans le tampon global.* +* repeat = indice d'utilisations successives du générateur. * +* line = ligne de rendu à compléter. * +* data = éventuelle donnée complémentaire fournie. * +* * +* Description : Etablit dans une ligne de rendu le contenu représenté. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_undefined_instruction_ui_populate_line(const GTokenGenerator *generator, size_t index, size_t repeat, GBufferLine *line, void *data) +{ + GArchInstruction *instr; /* Version spécialisée */ + GBinContent *content; /* Contenu brut d'origine */ + mrange_t range; /* Emplacement couvert */ + char *key; /* Mot clef principal */ + + instr = G_ARCH_INSTRUCTION(generator); + content = G_BIN_CONTENT(data); + + /* Prologue */ + + if (g_arch_instruction_get_range(instr, &range)) + { + g_buffer_line_fill_physical(line, ACO_PHYSICAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&range)); + + g_buffer_line_fill_virtual(line, ACO_VIRTUAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&range)); + + g_buffer_line_fill_content(line, ACO_BINARY, content, &range, VMPA_NO_PHYSICAL); + + } + + /* Instruction proprement dite */ + + key = g_arch_instruction_get_keyword(instr); + + g_buffer_line_append_text(line, ACO_ASSEMBLY_HEAD, TRT_ERROR, SL(key), NULL, G_OBJECT(instr)); + + free(key); + +} diff --git a/src/arch/instructions/undefined-ui.h b/src/arch/instructions/undefined-ui.h new file mode 100644 index 0000000..8bc2d67 --- /dev/null +++ b/src/arch/instructions/undefined-ui.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * undefined-ui.h - prototypes pour les opérandes représentant des instructions indéfinies sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_OPERANDS_UNDEFINED_UI_H +#define _ARCH_OPERANDS_UNDEFINED_UI_H + + +#include "../../glibext/generator-int.h" + + + +/* Procède à l'initialisation de l'interface de génération. */ +void g_undefined_instruction_ui_token_generator_iface_init(GTokenGeneratorInterface *); + + + +#endif /* _ARCH_OPERANDS_UNDEFINED_UI_H */ diff --git a/src/arch/instructions/undefined.c b/src/arch/instructions/undefined.c index 15c63e7..75df493 100644 --- a/src/arch/instructions/undefined.c +++ b/src/arch/instructions/undefined.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * undefined.c - instructions au comportement non défini * - * Copyright (C) 2016-2019 Cyrille Bagard + * Copyright (C) 2016-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,63 +25,42 @@ #include <assert.h> +#include <string.h> #include <i18n.h> #include "undefined-int.h" -#include "../../core/columns.h" +#include "../../glibext/serialize-int.h" +/* ------------------------- INSTRUCTION INCONNUE / DONNEES ------------------------- */ + + /* Initialise la classe des instructions non définies. */ -static void g_undef_instruction_class_init(GUndefInstructionClass *); +static void g_undefined_instruction_class_init(GUndefinedInstructionClass *); /* Initialise une instance d'instruction non définie. */ -static void g_undef_instruction_init(GUndefInstruction *); +static void g_undefined_instruction_init(GUndefinedInstruction *); /* Supprime toutes les références externes. */ -static void g_undef_instruction_dispose(GUndefInstruction *); +static void g_undefined_instruction_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_undef_instruction_finalize(GUndefInstruction *); - -/* Indique l'encodage d'une instruction de façon détaillée. */ -static const char *g_undef_instruction_get_encoding(const GUndefInstruction *); - -/* Fournit le nom humain de l'instruction manipulée. */ -static const char *g_undef_instruction_get_keyword(const GUndefInstruction *); - - - -/* -------------------- CONSERVATION SUR DISQUE DES INSTRUCTIONS -------------------- */ - - -/* Charge une instruction depuis une mémoire tampon. */ -static bool g_undef_instruction_unserialize(GUndefInstruction *, GAsmStorage *, GBinFormat *, packed_buffer_t *); - -/* Sauvegarde une instruction dans une mémoire tampon. */ -static bool g_undef_instruction_serialize(GUndefInstruction *, GAsmStorage *, packed_buffer_t *); - - - -/* ------------------------ OFFRE DE CAPACITES DE GENERATION ------------------------ */ - - -/* Ajoute à un tampon GLib le contenu de l'instance spécifiée. */ -static void g_undef_instruction_print(GUndefInstruction *, GBufferLine *, size_t, size_t, const GBinContent *); +static void g_undefined_instruction_finalize(GObject *); /* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ -/* Charge un contenu depuis une mémoire tampon. */ -static bool g_undef_instruction_load(GUndefInstruction *, GObjectStorage *, packed_buffer_t *); +/* Indique l'encodage d'une instruction de façon détaillée. */ +static char *g_undefined_instruction_get_encoding(const GArchInstruction *); -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool g_undef_instruction_store(GUndefInstruction *, GObjectStorage *, packed_buffer_t *); +/* Fournit le nom humain de l'instruction manipulée. */ +static char *g_undefined_instruction_get_keyword(const GArchInstruction *); @@ -91,7 +70,9 @@ static bool g_undef_instruction_store(GUndefInstruction *, GObjectStorage *, pac /* Indique le type défini pour une instruction au comportement non défini. */ -G_DEFINE_TYPE(GUndefInstruction, g_undef_instruction, G_TYPE_ARCH_INSTRUCTION); +G_DEFINE_TYPE_WITH_CODE(GUndefinedInstruction, g_undefined_instruction, G_TYPE_ARCH_INSTRUCTION, + G_IMPLEMENT_INTERFACE_IF_SYM(g_token_generator_get_type, g_undefined_instruction_ui_token_generator_iface_init)); + /****************************************************************************** @@ -106,28 +87,20 @@ G_DEFINE_TYPE(GUndefInstruction, g_undef_instruction, G_TYPE_ARCH_INSTRUCTION); * * ******************************************************************************/ -static void g_undef_instruction_class_init(GUndefInstructionClass *klass) +static void g_undefined_instruction_class_init(GUndefinedInstructionClass *klass) { GObjectClass *object; /* Autre version de la classe */ GArchInstructionClass *instr; /* Encore une autre vision... */ object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_undef_instruction_dispose; - object->finalize = (GObjectFinalizeFunc)g_undef_instruction_finalize; + object->dispose = g_undefined_instruction_dispose; + object->finalize = g_undefined_instruction_finalize; instr = G_ARCH_INSTRUCTION_CLASS(klass); - instr->get_encoding = (get_instruction_encoding_fc)g_undef_instruction_get_encoding; - instr->get_keyword = (get_instruction_keyword_fc)g_undef_instruction_get_keyword; - - instr->unserialize = (unserialize_instruction_fc)g_undef_instruction_unserialize; - instr->serialize = (serialize_instruction_fc)g_undef_instruction_serialize; - - instr->print = (print_instruction_fc)g_undef_instruction_print; - - instr->load = (load_instruction_fc)g_undef_instruction_load; - instr->store = (store_instruction_fc)g_undef_instruction_store; + instr->get_encoding = g_undefined_instruction_get_encoding; + instr->get_keyword = g_undefined_instruction_get_keyword; } @@ -144,16 +117,22 @@ static void g_undef_instruction_class_init(GUndefInstructionClass *klass) * * ******************************************************************************/ -static void g_undef_instruction_init(GUndefInstruction *instr) +static void g_undefined_instruction_init(GUndefinedInstruction *instr) { - GET_UNDEF_INSTR_EXTRA(instr)->behavior = IEB_UNDEFINED; + undef_extra_data_t extra; /* Données insérées à consulter*/ + + extra = GET_UNDEF_INSTR_EXTRA(instr); + + extra.behavior = IEB_UNDEFINED; + + SET_UNDEF_INSTR_EXTRA(instr, &extra); } /****************************************************************************** * * -* Paramètres : instr = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -163,16 +142,16 @@ static void g_undef_instruction_init(GUndefInstruction *instr) * * ******************************************************************************/ -static void g_undef_instruction_dispose(GUndefInstruction *instr) +static void g_undefined_instruction_dispose(GObject *object) { - G_OBJECT_CLASS(g_undef_instruction_parent_class)->dispose(G_OBJECT(instr)); + G_OBJECT_CLASS(g_undefined_instruction_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : instr = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -182,9 +161,9 @@ static void g_undef_instruction_dispose(GUndefInstruction *instr) * * ******************************************************************************/ -static void g_undef_instruction_finalize(GUndefInstruction *instr) +static void g_undefined_instruction_finalize(GObject *object) { - G_OBJECT_CLASS(g_undef_instruction_parent_class)->finalize(G_OBJECT(instr)); + G_OBJECT_CLASS(g_undefined_instruction_parent_class)->finalize(object); } @@ -201,16 +180,14 @@ static void g_undef_instruction_finalize(GUndefInstruction *instr) * * ******************************************************************************/ -GArchInstruction *g_undef_instruction_new(InstrExpectedBehavior behavior) +GArchInstruction *g_undefined_instruction_new(InstrExpectedBehavior behavior) { GArchInstruction *result; /* Instruction à retourner */ - undef_extra_data_t *extra; /* Données insérées à modifier */ - result = g_object_new(G_TYPE_UNDEF_INSTRUCTION, NULL); + result = g_object_new(G_TYPE_UNDEFINED_INSTRUCTION, NULL); - extra = GET_UNDEF_INSTR_EXTRA(result); - - extra->behavior = behavior; + if (!g_undefined_instruction_create(G_UNDEFINED_INSTRUCTION(result), behavior)) + g_clear_object(&result); return result; @@ -219,90 +196,10 @@ GArchInstruction *g_undef_instruction_new(InstrExpectedBehavior behavior) /****************************************************************************** * * -* Paramètres : instr = instruction quelconque à consulter. * -* * -* Description : Indique l'encodage d'une instruction de façon détaillée. * -* * -* Retour : Description humaine de l'encodage utilisé. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static const char *g_undef_instruction_get_encoding(const GUndefInstruction *instr) -{ - const char *result; /* Description à retourner */ - - result = _("Undefined"); - - return result; - -} - - -/****************************************************************************** +* Paramètres : instr = instance à initialiser pleinement. * +* behavior = état réel du CPU après une passe de l'instruction.* * * -* Paramètres : instr = instruction d'assemblage à consulter. * -* * -* Description : Fournit le nom humain de l'instruction manipulée. * -* * -* Retour : Mot clef de bas niveau. * -* * -* Remarques : - * -* * -******************************************************************************/ - -const char *g_undef_instruction_get_keyword(const GUndefInstruction *instr) -{ - const char *result; /* Désignation à retourner */ - undef_extra_data_t *extra; /* Données insérées à consulter*/ - - extra = GET_UNDEF_INSTR_EXTRA(instr); - - switch (extra->behavior) - { - case IEB_NOP: - result = "nop"; - break; - - case IEB_UNDEFINED: - result = "undefined"; - break; - - case IEB_UNPREDICTABLE: - result = "unpredictable"; - break; - - case IEB_RESERVED: - result = "reserved"; - break; - - default: - assert(false); - result = NULL; - break; - - } - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* CONSERVATION SUR DISQUE DES INSTRUCTIONS */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : instr = instruction d'assemblage à consulter. * -* storage = mécanisme de sauvegarde à manipuler. * -* format = format binaire chargé associé à l'architecture. * -* pbuf = zone tampon à remplir. * -* * -* Description : Charge une instruction depuis une mémoire tampon. * +* Description : Met en place une instruction au comportement indéfini. * * * * Retour : Bilan de l'opération. * * * @@ -310,122 +207,24 @@ const char *g_undef_instruction_get_keyword(const GUndefInstruction *instr) * * ******************************************************************************/ -static bool g_undef_instruction_unserialize(GUndefInstruction *instr, GAsmStorage *storage, GBinFormat *format, packed_buffer_t *pbuf) +bool g_undefined_instruction_create(GUndefinedInstruction *instr, InstrExpectedBehavior behavior) { bool result; /* Bilan à retourner */ - GArchInstructionClass *parent; /* Classe parente à consulter */ - undef_extra_data_t *extra; /* Données insérées à consulter*/ - uint8_t val; /* Champ de bits manipulé */ - - parent = G_ARCH_INSTRUCTION_CLASS(g_undef_instruction_parent_class); + undef_extra_data_t extra; /* Données insérées à modifier */ - result = parent->unserialize(G_ARCH_INSTRUCTION(instr), storage, format, pbuf); + result = true; - if (result) - { - extra = GET_UNDEF_INSTR_EXTRA(instr); - - LOCK_GOBJECT_EXTRA(extra); - - result = extract_packed_buffer(pbuf, &val, sizeof(uint8_t), false); - extra->behavior = val; - - UNLOCK_GOBJECT_EXTRA(extra); - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : instr = instruction d'assemblage à consulter. * -* storage = mécanisme de sauvegarde à manipuler. * -* pbuf = zone tampon à remplir. * -* * -* Description : Sauvegarde une instruction dans une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_undef_instruction_serialize(GUndefInstruction *instr, GAsmStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - GArchInstructionClass *parent; /* Classe parente à consulter */ - undef_extra_data_t *extra; /* Données insérées à consulter*/ - - parent = G_ARCH_INSTRUCTION_CLASS(g_undef_instruction_parent_class); - - result = parent->serialize(G_ARCH_INSTRUCTION(instr), storage, pbuf); - - if (result) - { - extra = GET_UNDEF_INSTR_EXTRA(instr); - - LOCK_GOBJECT_EXTRA(extra); - - result = extend_packed_buffer(pbuf, (uint8_t []){ extra->behavior }, sizeof(uint8_t), false); + extra = GET_UNDEF_INSTR_EXTRA(instr); - UNLOCK_GOBJECT_EXTRA(extra); + extra.behavior = behavior; - } + SET_UNDEF_INSTR_EXTRA(instr, &extra); return result; } - -/* ---------------------------------------------------------------------------------- */ -/* OFFRE DE CAPACITES DE GENERATION */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : instr = instruction d'assemblage à représenter. * -* line = ligne de rendu à compléter. * -* index = indice de cette même ligne dans le tampon global. * -* repeat = indice d'utilisations successives du générateur. * -* content = éventuel contenu binaire brut à imprimer. * -* * -* Description : Ajoute à un tampon GLib le contenu de l'instance spécifiée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_undef_instruction_print(GUndefInstruction *instr, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content) -{ - GArchInstruction *base; /* Version de base */ - const char *key; /* Mot clef principal */ - size_t klen; /* Taille de ce mot clef */ - - base = G_ARCH_INSTRUCTION(instr); - - g_buffer_line_fill_phys(line, DLC_PHYSICAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&base->range)); - - g_buffer_line_fill_virt(line, DLC_VIRTUAL, MDS_32_BITS_UNSIGNED, get_mrange_addr(&base->range)); - - g_buffer_line_fill_content(line, DLC_BINARY, content, &base->range, VMPA_NO_PHYSICAL); - - /* Instruction proprement dite */ - - key = g_arch_instruction_get_keyword(base); - klen = strlen(key); - - g_buffer_line_append_text(line, DLC_ASSEMBLY_HEAD, key, klen, RTT_ERROR, NULL); - -} - - /****************************************************************************** * * * Paramètres : instr = instruction à consulter. * @@ -438,18 +237,14 @@ static void g_undef_instruction_print(GUndefInstruction *instr, GBufferLine *lin * * ******************************************************************************/ -InstrExpectedBehavior g_undef_instruction_get_behavior(const GUndefInstruction *instr) +InstrExpectedBehavior g_undefined_instruction_get_behavior(const GUndefinedInstruction *instr) { InstrExpectedBehavior result; /* Comportement à retourner */ - undef_extra_data_t *extra; /* Données insérées à consulter*/ + undef_extra_data_t extra; /* Données insérées à consulter*/ extra = GET_UNDEF_INSTR_EXTRA(instr); - LOCK_GOBJECT_EXTRA(extra); - - result = extra->behavior; - - UNLOCK_GOBJECT_EXTRA(extra); + result = extra.behavior; return result; @@ -464,41 +259,21 @@ InstrExpectedBehavior g_undef_instruction_get_behavior(const GUndefInstruction * /****************************************************************************** * * -* Paramètres : instr = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * +* Paramètres : instr = instruction quelconque à consulter. * * * -* Description : Charge un contenu depuis une mémoire tampon. * +* Description : Indique l'encodage d'une instruction de façon détaillée. * * * -* Retour : Bilan de l'opération. * +* Retour : Description humaine de l'encodage utilisé. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_undef_instruction_load(GUndefInstruction *instr, GObjectStorage *storage, packed_buffer_t *pbuf) +static char *g_undefined_instruction_get_encoding(const GArchInstruction *instr) { - bool result; /* Bilan à retourner */ - GArchInstructionClass *parent; /* Classe parente à consulter */ - undef_extra_data_t *extra; /* Données insérées à consulter*/ - uint8_t val; /* Champ de bits manipulé */ - - parent = G_ARCH_INSTRUCTION_CLASS(g_undef_instruction_parent_class); - - result = parent->load(G_ARCH_INSTRUCTION(instr), storage, pbuf); - - if (result) - { - extra = GET_UNDEF_INSTR_EXTRA(instr); - - LOCK_GOBJECT_EXTRA(extra); + char *result; /* Description à retourner */ - result = extract_packed_buffer(pbuf, &val, sizeof(uint8_t), false); - extra->behavior = val; - - UNLOCK_GOBJECT_EXTRA(extra); - - } + result = strdup(_("Undefined")); return result; @@ -507,37 +282,45 @@ static bool g_undef_instruction_load(GUndefInstruction *instr, GObjectStorage *s /****************************************************************************** * * -* Paramètres : instr = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* Paramètres : instr = instruction d'assemblage à consulter. * * * -* Description : Sauvegarde un contenu dans une mémoire tampon. * +* Description : Fournit le nom humain de l'instruction manipulée. * * * -* Retour : Bilan de l'opération. * +* Retour : Mot clef de bas niveau. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_undef_instruction_store(GUndefInstruction *instr, GObjectStorage *storage, packed_buffer_t *pbuf) +static char *g_undefined_instruction_get_keyword(const GArchInstruction *instr) { - bool result; /* Bilan à retourner */ - GArchInstructionClass *parent; /* Classe parente à consulter */ - undef_extra_data_t *extra; /* Données insérées à consulter*/ - - parent = G_ARCH_INSTRUCTION_CLASS(g_undef_instruction_parent_class); + char *result; /* Désignation à retourner */ + undef_extra_data_t extra; /* Données insérées à consulter*/ - result = parent->store(G_ARCH_INSTRUCTION(instr), storage, pbuf); + extra = GET_UNDEF_INSTR_EXTRA(instr); - if (result) + switch (extra.behavior) { - extra = GET_UNDEF_INSTR_EXTRA(instr); + case IEB_NOP: + result = strdup("nop"); + break; - LOCK_GOBJECT_EXTRA(extra); + case IEB_UNDEFINED: + result = strdup("undefined"); + break; - result = extend_packed_buffer(pbuf, (uint8_t []){ extra->behavior }, sizeof(uint8_t), false); + case IEB_UNPREDICTABLE: + result = strdup("unpredictable"); + break; - UNLOCK_GOBJECT_EXTRA(extra); + case IEB_RESERVED: + result = strdup("reserved"); + break; + + default: + assert(false); + result = NULL; + break; } diff --git a/src/arch/instructions/undefined.h b/src/arch/instructions/undefined.h index 8f35f35..d4b35f4 100644 --- a/src/arch/instructions/undefined.h +++ b/src/arch/instructions/undefined.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * undefined.h - prototypes pour les instructions au comportement non défini * - * Copyright (C) 2016-2019 Cyrille Bagard + * Copyright (C) 2016-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,27 +25,14 @@ #define _ARCH_INSTRUCTIONS_UNDEFINED_H -#include <glib-object.h> - - #include "../instruction.h" -#include "../vmpa.h" - +#include "../../glibext/helpers.h" -#define G_TYPE_UNDEF_INSTRUCTION g_undef_instruction_get_type() -#define G_UNDEF_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_UNDEF_INSTRUCTION, GUndefInstruction)) -#define G_IS_UNDEF_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_UNDEF_INSTRUCTION)) -#define G_UNDEF_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_UNDEF_INSTRUCTION, GUndefInstructionClass)) -#define G_IS_UNDEF_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_UNDEF_INSTRUCTION)) -#define G_UNDEF_INSTRUCTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_UNDEF_INSTRUCTION, GUndefInstructionClass)) +#define G_TYPE_UNDEFINED_INSTRUCTION (g_undefined_instruction_get_type()) -/* Définition générique d'une instruction au comportement non défini (instance) */ -typedef struct _GUndefInstruction GUndefInstruction; - -/* Définition générique d'une instruction au comportement non défini (classe) */ -typedef struct _GUndefInstructionClass GUndefInstructionClass; +DECLARE_GTYPE(GUndefinedInstruction, g_undefined_instruction, G, UNDEFINED_INSTRUCTION); /* Etat précis de l'instruction */ @@ -59,14 +46,11 @@ typedef enum _InstrExpectedBehavior } InstrExpectedBehavior; -/* Indique le type défini pour une instruction au comportement non défini. */ -GType g_undef_instruction_get_type(void); - /* Crée une instruction au comportement nominalement indéfini. */ -GArchInstruction *g_undef_instruction_new(InstrExpectedBehavior); +GArchInstruction *g_undefined_instruction_new(InstrExpectedBehavior); /* Indique le type de conséquences réél de l'instruction. */ -InstrExpectedBehavior g_undef_instruction_get_behavior(const GUndefInstruction *); +InstrExpectedBehavior g_undefined_instruction_get_behavior(const GUndefinedInstruction *); diff --git a/src/arch/operand-int.h b/src/arch/operand-int.h index e6c1232..10c079b 100644 --- a/src/arch/operand-int.h +++ b/src/arch/operand-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * operand-int.h - prototypes pour la définition générique interne des opérandes * - * Copyright (C) 2008-2020 Cyrille Bagard + * Copyright (C) 2008-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,13 +26,23 @@ #include "operand.h" +#include "../common/szbin.h" +#include "../glibext/objhole-int.h" + + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +//typedef bool (* arch_operand_to_string_fc) (const GArchOperand *operand, unsigned int flags, sized_binary_t *); + + + +#if 0 #include <stdbool.h> #include "../analysis/storage/storage.h" -#include "../glibext/objhole.h" @@ -71,46 +81,29 @@ typedef bool (* load_operand_fc) (GArchOperand *, GObjectStorage *, packed_buffe typedef bool (* store_operand_fc) (GArchOperand *, GObjectStorage *, packed_buffer_t *); -/* Informations glissées dans la structure GObject de GArchOperand */ -typedef struct _operand_extra_data_t -{ - ArchOperandFlag flags; /* Informations diverses */ -} operand_extra_data_t; -/* Encapsulation avec un verrou d'accès */ -typedef union _operand_obj_extra_t -{ - operand_extra_data_t data; /* Données embarquées */ - lockable_obj_extra_t lockable; /* Gestion d'accès aux fanions */ +#endif -} operand_obj_extra_t; /* Définition générique d'un opérande d'architecture (instance) */ struct _GArchOperand { - GObject parent; /* A laisser en premier */ - -#if 1 //__SIZEOF_INT__ == __SIZEOF_LONG__ - - /** - * L'inclusion des informations suivantes dépend de l'architecture. - * - * Si la structure GObject possède un trou, on remplit de préférence - * ce dernier. - */ - - operand_obj_extra_t extra; /* Externalisation embarquée */ - -#endif + GThickObject parent; /* A laisser en premier */ }; /* Définition générique d'un opérande d'architecture (classe) */ struct _GArchOperandClass { - GObjectClass parent; /* A laisser en premier */ + GThickObjectClass parent; /* A laisser en premier */ + + + + + +#if 0 operand_compare_fc compare; /* Comparaison d'opérandes */ find_inner_operand_fc find_inner; /* Définition d'un chemin */ @@ -128,29 +121,57 @@ struct _GArchOperandClass load_operand_fc load; /* Chargement depuis un tampon */ store_operand_fc store; /* Conservation dans un tampon */ +#endif + }; + /** * Accès aux informations éventuellement déportées. */ -#if 1 //__SIZEOF_INT__ == __SIZEOF_LONG__ -# define GET_ARCH_OP_EXTRA(op) (operand_extra_data_t *)&op->extra +#define ARCH_OPERAND_EXTRA_DATA(mx) \ + \ + unsigned int reserved : GOBJECT_RESERVED_EXTRA_BITS; \ + \ + /** \ + * ArchOperandFlag \ + */ \ + unsigned int flags : mx; -#else -# define GET_ARCH_OP_EXTRA(op) GET_GOBJECT_EXTRA(G_OBJECT(op), operand_extra_data_t) +/* Informations glissées dans la structure GObject de GArchOperand */ +typedef struct _operand_extra_data_t +{ + ARCH_OPERAND_EXTRA_DATA(3); /* Informations pour l'opérande*/ + +} operand_extra_data_t; + + +#define GET_ARCH_OP_EXTRA(op) \ + GET_GOBJECT_EXTRA(op, operand_extra_data_t) + +#define SET_ARCH_OP_EXTRA(op, data) \ + SET_GOBJECT_EXTRA(op, operand_extra_data_t, data) + + + + + + + + +#if 0 -#endif /* Ajoute une information complémentaire à un opérande. */ -bool _g_arch_operand_set_flag(GArchOperand *, ArchOperandFlag, bool); +//bool _g_arch_operand_set_flag(GArchOperand *, ArchOperandFlag, bool); /* Retire une information complémentaire à un opérande. */ -bool _g_arch_operand_unset_flag(GArchOperand *, ArchOperandFlag, bool); +//bool _g_arch_operand_unset_flag(GArchOperand *, ArchOperandFlag, bool); @@ -190,5 +211,7 @@ bool g_arch_operand_store_generic_fixed(GArchOperand *, GObjectStorage *, packed bool g_arch_operand_store_generic_variadic(GArchOperand *, GObjectStorage *, packed_buffer_t *); +#endif + #endif /* _ARCH_OPERAND_INT_H */ diff --git a/src/arch/operand-ui-int.h b/src/arch/operand-ui-int.h new file mode 100644 index 0000000..c1173f3 --- /dev/null +++ b/src/arch/operand-ui-int.h @@ -0,0 +1,52 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * operand-ui-int.h - prototypes pour la définition générique interne des opérandes sous forme graphique + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_OPERAND_UI_INT_H +#define _ARCH_OPERAND_UI_INT_H + + +#include "operand-ui.h" + + + +/* Traduit un opérande en version humainement lisible. */ +typedef void (* print_operand_ui_fc) (const GArchOperandUI *, GBufferLine *); + +/* Construit un petit résumé concis de l'opérande. */ +typedef char * (* build_operand_ui_tooltip_fc) (const GArchOperandUI *, const GLoadedBinary *); + + + +/* Définition générique d'un opérande d'architecture (interface) */ +struct _GArchOperandUIInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + print_operand_ui_fc print; /* Texte humain équivalent */ + build_operand_ui_tooltip_fc build_tooltip; /* Définition de description*/ + +}; + + + +#endif /* _ARCH_OPERAND_UI_INT_H */ diff --git a/src/arch/operand-ui.c b/src/arch/operand-ui.c new file mode 100644 index 0000000..ff7ad1b --- /dev/null +++ b/src/arch/operand-ui.c @@ -0,0 +1,111 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * operand-ui.c - gestion générique des opérandes sous forme graphique + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "operand-ui.h" + + +#include "operand-ui-int.h" + + + +/* Procède à l'initialisation de l'interface d'opérande UI. */ +static void g_arch_operand_ui_default_init(GArchOperandUIInterface *); + + + +/* Indique le type défini pour un opérande d'architecture avec représentation graphique. */ +G_DEFINE_INTERFACE(GArchOperandUI, g_arch_operand_ui, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'opérande UI. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_arch_operand_ui_default_init(GArchOperandUIInterface *iface) +{ + iface->print = NULL; + iface->build_tooltip = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : operand = opérande à traiter. * +* line = ligne tampon où imprimer l'opérande donné. * +* * +* Description : Traduit un opérande en version humainement lisible. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_arch_operand_ui_print(const GArchOperandUI *operand, GBufferLine *line) +{ + GArchOperandUIInterface *iface; /* Interface utilisée */ + + iface = G_ARCH_OPERAND_UI_GET_IFACE(operand); + + iface->print(operand, line); + +} + + +/****************************************************************************** +* * +* Paramètres : operand = opérande à consulter. * +* binary = informations relatives au binaire chargé. * +* * +* Description : Construit un petit résumé concis de l'opérande. * +* * +* Retour : Chaîne de caractères à libérer après usage ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_arch_operand_ui_build_tooltip(const GArchOperandUI *operand, const GLoadedBinary *binary) +{ + char *result; /* Description à retourner */ + GArchOperandUIInterface *iface; /* Interface utilisée */ + + iface = G_ARCH_OPERAND_UI_GET_IFACE(operand); + + if (iface->build_tooltip != NULL) + result = iface->build_tooltip(operand, binary); + else + result = NULL; + + return result; + +} diff --git a/src/arch/operand-ui.h b/src/arch/operand-ui.h new file mode 100644 index 0000000..b197645 --- /dev/null +++ b/src/arch/operand-ui.h @@ -0,0 +1,54 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * operand-ui.h - prototypes pour la gestion générique des opérandes sous forme graphique + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_OPERAND_UI_H +#define _ARCH_OPERAND_UI_H + + +#include "../glibext/bufferline.h" +#include "../glibext/helpers.h" + + + +#define G_TYPE_ARCH_OPERAND_UI (g_arch_operand_ui_get_type()) + +DECLARE_INTERFACE(GArchOperandUI, g_arch_operand_ui, G, ARCH_OPERAND_UI); + + + +// TODO : REMME +//typedef void *GBufferLine; +typedef void *GLoadedBinary; +///// + + + +/* Traduit un opérande en version humainement lisible. */ +void g_arch_operand_ui_print(const GArchOperandUI *, GBufferLine *); + +/* Construit un petit résumé concis de l'opérande. */ +char *g_arch_operand_ui_build_tooltip(const GArchOperandUI *, const GLoadedBinary *); + + + +#endif /* _ARCH_OPERAND_UI_H */ diff --git a/src/arch/operand.c b/src/arch/operand.c index 0f5ffd5..d3e837c 100644 --- a/src/arch/operand.c +++ b/src/arch/operand.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * operand.c - gestion générique des opérandes * - * Copyright (C) 2008-2020 Cyrille Bagard + * Copyright (C) 2008-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,17 +25,16 @@ #include <assert.h> -#include <malloc.h> -#include <string.h> #include "operand-int.h" -#include "storage.h" -#include "../analysis/storage/serialize-int.h" #include "../common/fnv1a.h" #include "../common/sort.h" -#include "../core/logs.h" +#include "../glibext/comparable-int.h" +#include "../glibext/hashable-int.h" +#include "../glibext/serialize-int.h" #include "../glibext/singleton-int.h" +#include "../glibext/strbuilder-int.h" @@ -45,66 +44,67 @@ /* Initialise la classe générique des opérandes. */ static void g_arch_operand_class_init(GArchOperandClass *); -/* Initialise une instance d'opérande d'architecture. */ -static void g_arch_operand_init(GArchOperand *); +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_arch_operand_comparable_object_iface_init(GComparableObjectInterface *); -/* Procède à l'initialisation de l'interface de singleton. */ -static void g_arch_operand_singleton_init(GSingletonCandidateInterface *); +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_arch_operand_hashable_object_iface_init(GHashableObjectInterface *); /* Procède à l'initialisation de l'interface de sérialisation. */ -static void g_arch_operand_serializable_init(GSerializableObjectInterface *); +static void g_arch_operand_serializable_object_iface_init(GSerializableObjectInterface *); + +/* Procède à l'initialisation de l'interface de rassemblement. */ +static void g_arch_operand_singleton_candidate_iface_init(GSingletonCandidateInterface *); + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void g_arch_operand_string_builder_iface_init(GStringBuilderInterface *); + +/* Initialise une instance d'opérande d'architecture. */ +static void g_arch_operand_init(GArchOperand *); /* Supprime toutes les références externes. */ -static void g_arch_operand_dispose(GArchOperand *); +static void g_arch_operand_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_arch_operand_finalize(GArchOperand *); +static void g_arch_operand_finalize(GObject *); -/* Compare un opérande avec un autre. */ -static int _g_arch_operand_compare(const GArchOperand *, const GArchOperand *, bool); +/* ---------------------- COMPARAISON DETAILLEE DE DEUX OBJETS ---------------------- */ -/* ------------------------ CONTROLE DU VOLUME DES INSTANCES ------------------------ */ +/* Réalise une comparaison étendue entre objets. */ +static int g_arch_operand_compare(const GComparableObject *, const GComparableObject *); -/* Fournit une liste de candidats embarqués par un candidat. */ -GArchOperand **g_arch_operand_list_inner_instances(const GArchOperand *, size_t *); -/* Met à jour une liste de candidats embarqués par un candidat. */ -void g_arch_operand_update_inner_instances(GArchOperand *, GArchOperand **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint _g_arch_operand_hash(const GArchOperand *, bool); +/* ---------------------- CALCUL D'UNE EMPREINTE DE L'INSTANCE ---------------------- */ -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint g_arch_operand_hash(const GArchOperand *); -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean g_arch_operand_is_equal(const GArchOperand *, const GArchOperand *); +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint g_arch_operand_hash(const GHashableObject *); -/* Marque un candidat comme figé. */ -static void g_arch_operand_set_read_only(GArchOperand *); -/* Indique si le candidat est figé. */ -static bool g_arch_operand_is_read_only(GArchOperand *); +/* ------------------- MECANISMES DE CONSERVATION ET RESTAURATION ------------------- */ -/* -------------------- CONSERVATION ET RECHARGEMENT DES DONNEES -------------------- */ +/* Charge un objet depuis un flux de données. */ +static bool g_arch_operand_load(GSerializableObject *, GObjectStorage *, int); +/* Sauvegarde un objet dans un flux de données. */ +static bool g_arch_operand_store(const GSerializableObject *, GObjectStorage *, int); -/* Charge un contenu depuis une mémoire tampon. */ -static bool _g_arch_operand_load(GArchOperand *, GObjectStorage *, packed_buffer_t *); -/* Charge un contenu depuis une mémoire tampon. */ -static bool g_arch_operand_load(GArchOperand *, GObjectStorage *, packed_buffer_t *); -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool _g_arch_operand_store(GArchOperand *, GObjectStorage *, packed_buffer_t *); +/* ------------------------ CONTROLE DU VOLUME DES INSTANCES ------------------------ */ -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool g_arch_operand_store(GArchOperand *, GObjectStorage *, packed_buffer_t *); + +/* Marque un candidat comme figé. */ +static void g_arch_operand_mark_as_read_only(GSingletonCandidate *); + +/* Indique si le candidat est figé. */ +static bool g_arch_operand_is_read_only(const GSingletonCandidate *); @@ -114,9 +114,12 @@ static bool g_arch_operand_store(GArchOperand *, GObjectStorage *, packed_buffer /* Indique le type défini pour un opérande d'architecture. */ -G_DEFINE_TYPE_WITH_CODE(GArchOperand, g_arch_operand, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_SINGLETON_CANDIDATE, g_arch_operand_singleton_init) - G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_arch_operand_serializable_init)); +G_DEFINE_TYPE_WITH_CODE(GArchOperand, g_arch_operand, G_TYPE_THICK_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_COMPARABLE_OBJECT, g_arch_operand_comparable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_HASHABLE_OBJECT, g_arch_operand_hashable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_arch_operand_serializable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_SINGLETON_CANDIDATE, g_arch_operand_singleton_candidate_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_STRING_BUILDER, g_arch_operand_string_builder_iface_init)); /****************************************************************************** @@ -134,30 +137,20 @@ G_DEFINE_TYPE_WITH_CODE(GArchOperand, g_arch_operand, G_TYPE_OBJECT, static void g_arch_operand_class_init(GArchOperandClass *klass) { GObjectClass *object; /* Autre version de la classe */ - GArchOperandClass *operand; /* Encore une autre vision... */ object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_arch_operand_dispose; - object->finalize = (GObjectFinalizeFunc)g_arch_operand_finalize; - - operand = G_ARCH_OPERAND_CLASS(klass); - - operand->compare = (operand_compare_fc)_g_arch_operand_compare; - - operand->hash = _g_arch_operand_hash; - - operand->load = (load_operand_fc)_g_arch_operand_load; - operand->store = (store_operand_fc)_g_arch_operand_store; + object->dispose = g_arch_operand_dispose; + object->finalize = g_arch_operand_finalize; } /****************************************************************************** * * -* Paramètres : operand = instance à initialiser. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Initialise une instance d'opérande d'architecture. * +* Description : Procède à l'initialisation de l'interface de comparaison. * * * * Retour : - * * * @@ -165,13 +158,9 @@ static void g_arch_operand_class_init(GArchOperandClass *klass) * * ******************************************************************************/ -static void g_arch_operand_init(GArchOperand *operand) +static void g_arch_operand_comparable_object_iface_init(GComparableObjectInterface *iface) { - operand_extra_data_t *extra; /* Données insérées à modifier */ - - extra = GET_ARCH_OP_EXTRA(operand); - - INIT_GOBJECT_EXTRA_LOCK(extra); + iface->compare = g_arch_operand_compare; } @@ -180,7 +169,7 @@ static void g_arch_operand_init(GArchOperand *operand) * * * Paramètres : iface = interface GLib à initialiser. * * * -* Description : Procède à l'initialisation de l'interface de singleton. * +* Description : Procède à l'initialisation de l'interface de détermination. * * * * Retour : - * * * @@ -188,16 +177,9 @@ static void g_arch_operand_init(GArchOperand *operand) * * ******************************************************************************/ -static void g_arch_operand_singleton_init(GSingletonCandidateInterface *iface) +static void g_arch_operand_hashable_object_iface_init(GHashableObjectInterface *iface) { - iface->list_inner = (list_inner_instances_fc)g_arch_operand_list_inner_instances; - iface->update_inner = (update_inner_instances_fc)g_arch_operand_update_inner_instances; - - iface->hash = (hash_candidate_fc)g_arch_operand_hash; - iface->is_equal = (is_candidate_equal_fc)g_arch_operand_is_equal; - - iface->set_ro = (set_candidate_ro_fc)g_arch_operand_set_read_only; - iface->is_ro = (is_candidate_ro_fc)g_arch_operand_is_read_only; + iface->hash = g_arch_operand_hash; } @@ -214,38 +196,19 @@ static void g_arch_operand_singleton_init(GSingletonCandidateInterface *iface) * * ******************************************************************************/ -static void g_arch_operand_serializable_init(GSerializableObjectInterface *iface) +static void g_arch_operand_serializable_object_iface_init(GSerializableObjectInterface *iface) { - iface->load = (load_serializable_object_cb)g_arch_operand_load; - iface->store = (store_serializable_object_cb)g_arch_operand_store; + iface->load = g_arch_operand_load; + iface->store = g_arch_operand_store; } /****************************************************************************** * * -* Paramètres : operand = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_arch_operand_dispose(GArchOperand *operand) -{ - G_OBJECT_CLASS(g_arch_operand_parent_class)->dispose(G_OBJECT(operand)); - -} - - -/****************************************************************************** -* * -* Paramètres : operand = instance d'objet GLib à traiter. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Procède à la libération totale de la mémoire. * +* Description : Procède à l'initialisation de l'interface de rassemblement. * * * * Retour : - * * * @@ -253,147 +216,59 @@ static void g_arch_operand_dispose(GArchOperand *operand) * * ******************************************************************************/ -static void g_arch_operand_finalize(GArchOperand *operand) +static void g_arch_operand_singleton_candidate_iface_init(GSingletonCandidateInterface *iface) { - G_OBJECT_CLASS(g_arch_operand_parent_class)->finalize(G_OBJECT(operand)); - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier opérande à consulter. * -* b = second opérande à consulter. * -* lock = précise le besoin en verrouillage. * -* * -* Description : Compare un opérande avec un autre. * -* * -* Retour : Bilan de la comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static int _g_arch_operand_compare(const GArchOperand *a, const GArchOperand *b, bool lock) -{ - int result; /* Bilan à faire remonter */ - operand_extra_data_t *ea; /* Données insérées à consulter*/ - operand_extra_data_t *eb; /* Données insérées à consulter*/ - - assert(!lock); - - ea = GET_ARCH_OP_EXTRA(a); - eb = GET_ARCH_OP_EXTRA(b); + iface->list_inner = NULL; + iface->update_inner = NULL; - result = sort_unsigned_long(ea->flags, eb->flags); - - return result; + iface->mark_as_ro = g_arch_operand_mark_as_read_only; + iface->is_ro = g_arch_operand_is_read_only; } /****************************************************************************** * * -* Paramètres : a = premier opérande à consulter. * -* b = second opérande à consulter. * -* * -* Description : Compare un opérande avec un autre. * -* * -* Retour : Bilan de la comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int g_arch_operand_compare(const GArchOperand *a, const GArchOperand *b) -{ - int result; /* Bilan à faire remonter */ - GType type_a; /* Type de l'object A */ - GType type_b; /* Type de l'object B */ - - type_a = G_OBJECT_TYPE(G_OBJECT(a)); - type_b = G_OBJECT_TYPE(G_OBJECT(b)); - - assert(sizeof(GType) <= sizeof(unsigned long)); - - result = sort_unsigned_long(type_a, type_b); - - if (result == 0) - result = G_ARCH_OPERAND_GET_CLASS(a)->compare(a, b, true); - - return result; - -} - - -/****************************************************************************** +* Paramètres : iface = interface GLib à initialiser. * * * -* Paramètres : operand = opérande à consulter. * -* target = instruction à venir retrouver. * +* Description : Procède à l'initialisation de l'interface d'exportation. * * * -* Description : Détermine le chemin conduisant à un opérande interne. * -* * -* Retour : Chemin d'accès à l'opérande ou NULL en cas d'absence. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -char *g_arch_operand_find_inner_operand_path(const GArchOperand *operand, const GArchOperand *target) +static void g_arch_operand_string_builder_iface_init(GStringBuilderInterface *iface) { - char *result; /* Chemin à retourner */ - GArchOperandClass *class; /* Classe associée à l'objet */ - - class = G_ARCH_OPERAND_GET_CLASS(operand); - - if (class->find_inner != NULL) - result = class->find_inner(operand, target); - - else - result = NULL; - - return result; + iface->to_string = NULL; } /****************************************************************************** * * -* Paramètres : operand = opérande à consulter. * -* path = chemin d'accès à un opérande à retrouver. * +* Paramètres : operand = instance à initialiser. * * * -* Description : Obtient l'opérande correspondant à un chemin donné. * +* Description : Initialise une instance d'opérande d'architecture. * * * -* Retour : Opérande trouvé ou NULL en cas d'échec. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GArchOperand *g_arch_operand_get_inner_operand_from_path(const GArchOperand *operand, const char *path) +static void g_arch_operand_init(GArchOperand *operand) { - GArchOperand *result; /* Opérande trouvée à renvoyer */ - GArchOperandClass *class; /* Classe associée à l'objet */ - - class = G_ARCH_OPERAND_GET_CLASS(operand); - - if (class->get_inner != NULL) - result = class->get_inner(operand, path); - - else - result = NULL; - - return result; } /****************************************************************************** * * -* Paramètres : operand = opérande à traiter. * -* line = ligne tampon où imprimer l'opérande donné. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Traduit un opérande en version humainement lisible. * +* Description : Supprime toutes les références externes. * * * * Retour : - * * * @@ -401,54 +276,36 @@ GArchOperand *g_arch_operand_get_inner_operand_from_path(const GArchOperand *ope * * ******************************************************************************/ -void g_arch_operand_print(const GArchOperand *operand, GBufferLine *line) +static void g_arch_operand_dispose(GObject *object) { - G_ARCH_OPERAND_GET_CLASS(operand)->print(operand, line); + G_OBJECT_CLASS(g_arch_operand_parent_class)->dispose(object); } -#ifdef INCLUDE_GTK_SUPPORT - - /****************************************************************************** * * -* Paramètres : operand = opérande à consulter. * -* binary = informations relatives au binaire chargé. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Construit un petit résumé concis de l'opérande. * +* Description : Procède à la libération totale de la mémoire. * * * -* Retour : Chaîne de caractères à libérer après usage ou NULL. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -char *g_arch_operand_build_tooltip(const GArchOperand *operand, const GLoadedBinary *binary) +static void g_arch_operand_finalize(GObject *object) { - char *result; /* Description à retourner */ - GArchOperandClass *class; /* Classe associée à l'objet */ - - class = G_ARCH_OPERAND_GET_CLASS(operand); - - if (class->build_tooltip != NULL) - result = class->build_tooltip(operand, binary); - else - result = NULL; - - return result; + G_OBJECT_CLASS(g_arch_operand_parent_class)->finalize(object); } -#endif - - /****************************************************************************** * * * Paramètres : operand = opérande à venir modifier. * * flag = drapeau d'information complémentaire à planter. * -* lock = indique un besoin de verrouillage des données. * * * * Description : Ajoute une information complémentaire à un opérande. * * * @@ -458,48 +315,20 @@ char *g_arch_operand_build_tooltip(const GArchOperand *operand, const GLoadedBin * * ******************************************************************************/ -bool _g_arch_operand_set_flag(GArchOperand *operand, ArchOperandFlag flag, bool lock) +bool g_arch_operand_set_flag(GArchOperand *operand, ArchOperandFlag flag) { bool result; /* Bilan à retourner */ - operand_extra_data_t *extra; /* Données insérées à modifier */ + operand_extra_data_t extra; /* Données insérées à modifier */ assert(flag <= AOF_HIGH_USER); extra = GET_ARCH_OP_EXTRA(operand); - if (lock) - LOCK_GOBJECT_EXTRA(extra); - - result = !(extra->flags & flag); - - extra->flags |= flag; - - if (lock) - UNLOCK_GOBJECT_EXTRA(extra); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à venir modifier. * -* flag = drapeau d'information complémentaire à planter. * -* * -* Description : Ajoute une information complémentaire à un opérande. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ + result = !(extra.flags & flag); -bool g_arch_operand_set_flag(GArchOperand *operand, ArchOperandFlag flag) -{ - bool result; /* Bilan à retourner */ + extra.flags |= flag; - result = _g_arch_operand_set_flag(operand, flag, true); + SET_ARCH_OP_EXTRA(operand, &extra); return result; @@ -510,7 +339,6 @@ bool g_arch_operand_set_flag(GArchOperand *operand, ArchOperandFlag flag) * * * Paramètres : operand = opérande à venir modifier. * * flag = drapeau d'information complémentaire à planter. * -* lock = indique un besoin de verrouillage des données. * * * * Description : Retire une information complémentaire à un opérande. * * * @@ -520,46 +348,20 @@ bool g_arch_operand_set_flag(GArchOperand *operand, ArchOperandFlag flag) * * ******************************************************************************/ -bool _g_arch_operand_unset_flag(GArchOperand *operand, ArchOperandFlag flag, bool lock) +bool g_arch_operand_unset_flag(GArchOperand *operand, ArchOperandFlag flag) { bool result; /* Bilan à retourner */ - operand_extra_data_t *extra; /* Données insérées à modifier */ + operand_extra_data_t extra; /* Données insérées à modifier */ assert(flag <= AOF_HIGH_USER); extra = GET_ARCH_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); - - result = (extra->flags & flag); - - extra->flags &= ~flag; - - UNLOCK_GOBJECT_EXTRA(extra); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à venir modifier. * -* flag = drapeau d'information complémentaire à planter. * -* * -* Description : Retire une information complémentaire à un opérande. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ + result = (extra.flags & flag); -bool g_arch_operand_unset_flag(GArchOperand *operand, ArchOperandFlag flag) -{ - bool result; /* Bilan à retourner */ + extra.flags &= ~flag; - result = _g_arch_operand_unset_flag(operand, flag, true); + SET_ARCH_OP_EXTRA(operand, &extra); return result; @@ -582,17 +384,13 @@ bool g_arch_operand_unset_flag(GArchOperand *operand, ArchOperandFlag flag) bool g_arch_operand_has_flag(const GArchOperand *operand, ArchOperandFlag flag) { bool result; /* Bilan à retourner */ - operand_extra_data_t *extra; /* Données insérées à modifier */ + operand_extra_data_t extra; /* Données insérées à modifier */ assert(flag <= AOF_HIGH_USER); extra = GET_ARCH_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); - - result = (extra->flags & flag); - - UNLOCK_GOBJECT_EXTRA(extra); + result = (extra.flags & flag); return result; @@ -614,15 +412,11 @@ bool g_arch_operand_has_flag(const GArchOperand *operand, ArchOperandFlag flag) ArchOperandFlag g_arch_operand_get_flags(const GArchOperand *operand) { ArchOperandFlag result; /* Fanions à retourner */ - operand_extra_data_t *extra; /* Données insérées à modifier */ + operand_extra_data_t extra; /* Données insérées à modifier */ extra = GET_ARCH_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); - - result = extra->flags; - - UNLOCK_GOBJECT_EXTRA(extra); + result = extra.flags; return result; @@ -630,39 +424,40 @@ ArchOperandFlag g_arch_operand_get_flags(const GArchOperand *operand) -/* ---------------------------------------------------------------------------------- */ -/* CONTROLE DU VOLUME DES INSTANCES */ -/* ---------------------------------------------------------------------------------- */ + + + + +#if 0 + + /****************************************************************************** * * -* Paramètres : operand = objet dont l'instance se veut unique. * -* count = quantité d'instances à l'unicité internes. * +* Paramètres : operand = opérande à consulter. * +* target = instruction à venir retrouver. * * * -* Description : Fournit une liste de candidats embarqués par un candidat. * +* Description : Détermine le chemin conduisant à un opérande interne. * * * -* Retour : Liste de candidats internes ou NULL si aucun. * +* Retour : Chemin d'accès à l'opérande ou NULL en cas d'absence. * * * * Remarques : - * * * ******************************************************************************/ -GArchOperand **g_arch_operand_list_inner_instances(const GArchOperand *operand, size_t *count) +char *g_arch_operand_find_inner_operand_path(const GArchOperand *operand, const GArchOperand *target) { - GArchOperand **result; /* Instances à retourner */ + char *result; /* Chemin à retourner */ GArchOperandClass *class; /* Classe associée à l'objet */ class = G_ARCH_OPERAND_GET_CLASS(operand); - if (class->list_inner == NULL) - { - *count = 0; - result = NULL; - } + if (class->find_inner != NULL) + result = class->find_inner(operand, target); else - result = class->list_inner(operand, count); + result = NULL; return result; @@ -671,105 +466,59 @@ GArchOperand **g_arch_operand_list_inner_instances(const GArchOperand *operand, /****************************************************************************** * * -* Paramètres : operand = objet dont l'instance se veut unique. * -* instances = liste de candidats internes devenus singletons. * -* count = quantité d'instances à l'unicité internes. * +* Paramètres : operand = opérande à consulter. * +* path = chemin d'accès à un opérande à retrouver. * * * -* Description : Met à jour une liste de candidats embarqués par un candidat. * +* Description : Obtient l'opérande correspondant à un chemin donné. * * * -* Retour : - * +* Retour : Opérande trouvé ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ -void g_arch_operand_update_inner_instances(GArchOperand *operand, GArchOperand **instances, size_t count) +GArchOperand *g_arch_operand_get_inner_operand_from_path(const GArchOperand *operand, const char *path) { + GArchOperand *result; /* Opérande trouvée à renvoyer */ GArchOperandClass *class; /* Classe associée à l'objet */ class = G_ARCH_OPERAND_GET_CLASS(operand); - if (class->update_inner == NULL) - assert(class->list_inner == NULL); + if (class->get_inner != NULL) + result = class->get_inner(operand, path); else - { - assert(class->list_inner != NULL); - class->update_inner(operand, instances, count); - } + result = NULL; + + return result; } -/****************************************************************************** -* * -* Paramètres : operand = objet dont l'instance se veut unique. * -* lock = précise le besoin en verrouillage. * -* * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * -* * -* Retour : Empreinte de l'élément représenté. * -* * -* Remarques : - * -* * -******************************************************************************/ +#endif -static guint _g_arch_operand_hash(const GArchOperand *operand, bool lock) -{ - guint result; /* Valeur à retourner */ - const char *name; /* Désignation du type d'object*/ - fnv64_t name_hash; /* Empreinte du nom */ - operand_extra_data_t *extra; /* Données insérées à modifier */ - assert(!lock); - name = G_OBJECT_TYPE_NAME(G_OBJECT(operand)); - name_hash = fnv_64a_hash(name); - result = (name_hash & 0xffffffff); - result ^= (name_hash >> 32); - extra = GET_ARCH_OP_EXTRA(operand); - result ^= extra->flags; - return result; -} -/****************************************************************************** -* * -* Paramètres : operand = objet dont l'instance se veut unique. * -* * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * -* * -* Retour : Empreinte de l'élément représenté. * -* * -* Remarques : - * -* * -******************************************************************************/ -static guint g_arch_operand_hash(const GArchOperand *operand) -{ - guint result; /* Valeur à retourner */ - GArchOperandClass *class; /* Classe associée à l'objet */ - - class = G_ARCH_OPERAND_GET_CLASS(operand); - - result = class->hash(operand, true); - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* COMPARAISON DETAILLEE DE DEUX OBJETS */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : operand = objet dont l'instance se veut unique. * -* other = second élément à analyser. * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Réalise une comparaison étendue entre objets. * * * * Retour : Bilan de la comparaison. * * * @@ -777,56 +526,56 @@ static guint g_arch_operand_hash(const GArchOperand *operand) * * ******************************************************************************/ -static gboolean g_arch_operand_is_equal(const GArchOperand *operand, const GArchOperand *other) +static int g_arch_operand_compare(const GComparableObject *object, const GComparableObject *other) { - gboolean result; /* Bilan à renvoyer */ - int ret; /* Bilan d'une comparaison */ + int result; /* Bilan à retourner */ + operand_extra_data_t extra_op; /* Données insérées à consulter*/ + operand_extra_data_t extra_other; /* Données insérées à consulter*/ - ret = g_arch_operand_compare(operand, other); + extra_op = GET_ARCH_OP_EXTRA(object); + extra_other = GET_ARCH_OP_EXTRA(other); - result = (ret == 0); + result = sort_unsigned_long(extra_op.flags, extra_other.flags); return result; } -/****************************************************************************** -* * -* Paramètres : operand = objet dont l'instance se veut unique. * -* * -* Description : Marque un candidat comme figé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_arch_operand_set_read_only(GArchOperand *operand) -{ - g_arch_operand_set_flag(operand, AOF_READ_ONLY); -} +/* ---------------------------------------------------------------------------------- */ +/* CALCUL D'UNE EMPREINTE DE L'INSTANCE */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : operand = objet dont l'instance se veut unique. * +* Paramètres : object = objet dont l'instance est à consulter. * * * -* Description : Indique si le candidat est figé. * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * * * -* Retour : true si le contenu du candidat ne peut plus être modifié. * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_arch_operand_is_read_only(GArchOperand *operand) +static guint g_arch_operand_hash(const GHashableObject *object) { - bool result; /* Etat à retourner */ + guint result; /* Valeur à retourner */ + const char *name; /* Désignation du type d'object*/ + fnv64_t name_hash; /* Empreinte du nom */ + operand_extra_data_t extra; /* Données insérées à consulter*/ - result = g_arch_operand_has_flag(operand, AOF_READ_ONLY); + name = G_OBJECT_TYPE_NAME(G_OBJECT(object)); + name_hash = fnv_64a_hash(name); + + result = (name_hash & 0xffffffff); + result ^= (name_hash >> 32); + + extra = GET_ARCH_OP_EXTRA(object); + + result ^= extra.flags; return result; @@ -835,17 +584,17 @@ static bool g_arch_operand_is_read_only(GArchOperand *operand) /* ---------------------------------------------------------------------------------- */ -/* CONSERVATION ET RECHARGEMENT DES DONNEES */ +/* MECANISMES DE CONSERVATION ET RESTAURATION */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : operand = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * +* Paramètres : object = élément GLib à constuire. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * * * -* Description : Charge un contenu depuis une mémoire tampon. * +* Description : Charge un objet depuis un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -853,22 +602,15 @@ static bool g_arch_operand_is_read_only(GArchOperand *operand) * * ******************************************************************************/ -static bool _g_arch_operand_load(GArchOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_arch_operand_load(GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ - operand_extra_data_t *extra; /* Données insérées à consulter*/ - uleb128_t value; /* Valeur ULEB128 à charger */ - - extra = GET_ARCH_OP_EXTRA(operand); + uleb128_t extra; /* Données embarquées */ - LOCK_GOBJECT_EXTRA(extra); - - result = unpack_uleb128(&value, pbuf); + result = load_uleb128(&extra, fd); if (result) - extra->flags = value; - - UNLOCK_GOBJECT_EXTRA(extra); + g_thick_object_set_extra(G_THICK_OBJECT(object), extra); return result; @@ -877,11 +619,11 @@ static bool _g_arch_operand_load(GArchOperand *operand, GObjectStorage *storage, /****************************************************************************** * * -* Paramètres : operand = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * +* Paramètres : object = élément GLib à consulter. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * * * -* Description : Charge un contenu depuis une mémoire tampon. * +* Description : Sauvegarde un objet dans un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -889,74 +631,69 @@ static bool _g_arch_operand_load(GArchOperand *operand, GObjectStorage *storage, * * ******************************************************************************/ -static bool g_arch_operand_load(GArchOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_arch_operand_store(const GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ - GArchOperandClass *class; /* Classe à activer */ + guint extra; /* Données embarquées */ - class = G_ARCH_OPERAND_GET_CLASS(operand); + extra = g_thick_object_get_extra(G_THICK_OBJECT(object)); - result = class->load(operand, storage, pbuf); + result = store_uleb128((uleb128_t []) { extra }, fd); return result; } + +/* ---------------------------------------------------------------------------------- */ +/* CONTROLE DU VOLUME DES INSTANCES */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Sauvegarde un contenu dans une mémoire tampon. * +* Description : Marque un candidat comme figé. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool _g_arch_operand_store(GArchOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) +static void g_arch_operand_mark_as_read_only(GSingletonCandidate *candidate) { - bool result; /* Bilan à retourner */ - operand_extra_data_t *extra; /* Données insérées à consulter*/ - - extra = GET_ARCH_OP_EXTRA(operand); - - LOCK_GOBJECT_EXTRA(extra); - - result = pack_uleb128((uleb128_t []){ extra->flags }, pbuf); + GArchOperand *operand; /* Version spécialisée */ - UNLOCK_GOBJECT_EXTRA(extra); + operand = G_ARCH_OPERAND(candidate); - return result; + g_arch_operand_set_flag(operand, AOF_READ_ONLY); } /****************************************************************************** * * -* Paramètres : operand = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* Paramètres : operand = objet dont l'instance se veut unique. * * * -* Description : Sauvegarde un contenu dans une mémoire tampon. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de l'opération. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_arch_operand_store(GArchOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_arch_operand_is_read_only(const GSingletonCandidate *candidate) { - bool result; /* Bilan à retourner */ - GArchOperandClass *class; /* Classe à activer */ + bool result; /* Etat à retourner */ + GArchOperand *operand; /* Version spécialisée */ - class = G_ARCH_OPERAND_GET_CLASS(operand); + operand = G_ARCH_OPERAND(candidate); - result = class->store(operand, storage, pbuf); + result = g_arch_operand_has_flag(operand, AOF_READ_ONLY); return result; diff --git a/src/arch/operand.h b/src/arch/operand.h index 234ee64..72a1b56 100644 --- a/src/arch/operand.h +++ b/src/arch/operand.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * operand.h - prototypes pour la gestion générique des opérandes * - * Copyright (C) 2008-2020 Cyrille Bagard + * Copyright (C) 2008-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,20 +25,16 @@ #define _ARCH_OPERAND_H -#include <glib-object.h> - +#include <stdbool.h> -#include "../common/packed.h" -#include "../format/format.h" -#include "../glibext/bufferline.h" +#include "../glibext/helpers.h" -/* ------------------------ DEFINITION D'OPERANDE QUELCONQUE ------------------------ */ +#define G_TYPE_ARCH_OPERAND (g_arch_operand_get_type()) -/* Depuis "../analysis/binary.h" : description de fichier binaire */ -typedef struct _GLoadedBinary GLoadedBinary; +DECLARE_GTYPE(GArchOperand, g_arch_operand, G, ARCH_OPERAND); /* Indications supplémentaires liées aux opérandes */ @@ -58,6 +54,41 @@ typedef enum _ArchOperandFlag #define AOF_USER_FLAG(n) (1 << (AOF_USER_BIT + n)) +/* Ajoute une information complémentaire à un opérande. */ +bool g_arch_operand_set_flag(GArchOperand *, ArchOperandFlag); + +/* Retire une information complémentaire à un opérande. */ +bool g_arch_operand_unset_flag(GArchOperand *, ArchOperandFlag); + +/* Détermine si un opérande possède un fanion particulier. */ +bool g_arch_operand_has_flag(const GArchOperand *, ArchOperandFlag); + +/* Fournit les particularités de l'opérande. */ +ArchOperandFlag g_arch_operand_get_flags(const GArchOperand *); + + + + +#if 0 + + +#include <glib-object.h> + + +#include "../common/packed.h" +#include "../format/format.h" +#include "../glibext/bufferline.h" + + + +/* ------------------------ DEFINITION D'OPERANDE QUELCONQUE ------------------------ */ + + +/* Depuis "../analysis/binary.h" : description de fichier binaire */ +typedef struct _GLoadedBinary GLoadedBinary; + + + #define G_TYPE_ARCH_OPERAND g_arch_operand_get_type() #define G_ARCH_OPERAND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_ARCH_OPERAND, GArchOperand)) #define G_IS_ARCH_OPERAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_ARCH_OPERAND)) @@ -95,18 +126,6 @@ char *g_arch_operand_build_tooltip(const GArchOperand *, const GLoadedBinary *); #endif -/* Ajoute une information complémentaire à un opérande. */ -bool g_arch_operand_set_flag(GArchOperand *, ArchOperandFlag); - -/* Retire une information complémentaire à un opérande. */ -bool g_arch_operand_unset_flag(GArchOperand *, ArchOperandFlag); - -/* Détermine si un opérande possède un fanion particulier. */ -bool g_arch_operand_has_flag(const GArchOperand *, ArchOperandFlag); - -/* Fournit les particularités de l'opérande. */ -ArchOperandFlag g_arch_operand_get_flags(const GArchOperand *); - /* --------------------- TRANSPOSITIONS VIA CACHE DES OPERANDES --------------------- */ @@ -116,5 +135,8 @@ ArchOperandFlag g_arch_operand_get_flags(const GArchOperand *); typedef struct _GAsmStorage GAsmStorage; +#endif + + #endif /* _ARCH_OPERAND_H */ diff --git a/src/arch/operands/Makefile.am b/src/arch/operands/Makefile.am index f2a8767..3d9cbde 100644 --- a/src/arch/operands/Makefile.am +++ b/src/arch/operands/Makefile.am @@ -1,26 +1,36 @@ -noinst_LTLIBRARIES = libarchoperands.la +noinst_LTLIBRARIES = libarchoperands.la libarchoperandsui.la + +# libarchoperands_la_SOURCES = \ +# feeder-int.h \ +# feeder.h feeder.c \ +# proxy-int.h \ +# proxy.h proxy.c \ +# rename-int.h \ +# rename.h rename.c \ +# target-int.h \ +# target.h target.c \ +# targetable-int.h \ +# targetable.h targetable.c libarchoperands_la_SOURCES = \ - feeder-int.h \ - feeder.h feeder.c \ immediate-int.h \ immediate.h immediate.c \ + known-int.h \ known.h known.c \ register-int.h \ - register.h register.c \ - proxy-int.h \ - proxy.h proxy.c \ - rename-int.h \ - rename.h rename.c \ - target-int.h \ - target.h target.c \ - targetable-int.h \ - targetable.h targetable.c + register.h register.c + +libarchoperands_la_CFLAGS = $(LIBGOBJ_CFLAGS) + +libarchoperandsui_la_SOURCES = \ + immediate-ui.h immediate-ui.c \ + known-ui.h known-ui.c \ + register-ui.h register-ui.c -libarchoperands_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) +libarchoperandsui_la_CFLAGS = $(LIBGTK4_CFLAGS) devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) -dev_HEADERS = $(libarchoperands_la_SOURCES:%c=) +dev_HEADERS = $(libarchoperands_la_SOURCES:%c=) $(libarchoperandsui_la_SOURCES:%c=) diff --git a/src/arch/operands/immediate-int.h b/src/arch/operands/immediate-int.h index d2313f5..3d60c7d 100644 --- a/src/arch/operands/immediate-int.h +++ b/src/arch/operands/immediate-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * immediate-int.h - définitions internes propres aux opérandes représentant des valeurs numériques * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -30,27 +30,8 @@ -/* Informations glissées dans la structure GObject de GArchOperand */ -typedef struct _immop_extra_data_t -{ - operand_extra_data_t parent; /* A laisser en premier */ - - MemoryDataSize size; /* Taille de l'opérande */ - - /** - * Les deux éléments suivants sont de type ImmOperandDisplay ; - * leur espace de conservation est réduit au maximum afin d'éviter - * un recouvrement . - */ - - unsigned int def_display : 3; /* Type par défaut d'affichage */ - unsigned int display : 3; /* Format général d'affichage */ - -} immop_extra_data_t; - - /* Définition d'un opérande de valeur numérique (instance) */ -struct _GImmOperand +struct _GImmediateOperand { GArchOperand parent; /* Instance parente */ @@ -59,7 +40,7 @@ struct _GImmOperand }; /* Définition d'un opérande de valeur numérique (classe) */ -struct _GImmOperandClass +struct _GImmediateOperandClass { GArchOperandClass parent; /* Classe parente */ @@ -70,15 +51,46 @@ struct _GImmOperandClass * Accès aux informations éventuellement déportées. */ -#if 1 //__SIZEOF_INT__ == __SIZEOF_LONG__ +/* Informations glissées dans la structure GObject de GArchOperand */ +typedef struct _immop_extra_data_t +{ + ARCH_OPERAND_EXTRA_DATA(3); /* Informations pour l'opérande*/ -# define GET_IMM_OP_EXTRA(op) ((immop_extra_data_t *)&((GArchOperand *)op)->extra) + /** + * MemoryDataSize + */ + unsigned int size : 4; /* Taille de l'opérande */ + + /** + * ImmOperandDisplay x 2 + */ + unsigned int def_display : 3; /* Type par défaut d'affichage */ + unsigned int display : 3; /* Format général d'affichage */ + +} immop_extra_data_t; + + +#define GET_IMM_OP_EXTRA(op) \ + GET_GOBJECT_EXTRA(op, immop_extra_data_t) + +#define SET_IMM_OP_EXTRA(op, data) \ + SET_GOBJECT_EXTRA(op, immop_extra_data_t, data) -#else -# define GET_IMM_OP_EXTRA(op) GET_GOBJECT_EXTRA(G_OBJECT(op), immop_extra_data_t) +/* Met en place un opérande réprésentant une valeur numérique. */ +bool g_immediate_operand_create_from_value(GImmediateOperand *, MemoryDataSize, uint64_t); + +/* Crée un opérande réprésentant une valeur numérique. */ +bool g_immediate_operand_create_from_data(GImmediateOperand *, MemoryDataSize, const GBinContent *, vmpa2t *, bool *, SourceEndian); + +/** + * La taille d'impression d'un opérande n'est pas VMPA_MAX_SIZE, + * mais 1 + 64 caractères + octet nul final en cas d'impression en binaire. + */ +#define IMM_MAX_SIZE 66 -#endif +/* Construit la chaîne de caractères correspondant à l'opérande. */ +size_t _g_immediate_operand_to_string(const GImmediateOperand *, ImmOperandDisplay, char [IMM_MAX_SIZE]); diff --git a/src/arch/operands/immediate-ui.c b/src/arch/operands/immediate-ui.c new file mode 100644 index 0000000..62058b3 --- /dev/null +++ b/src/arch/operands/immediate-ui.c @@ -0,0 +1,184 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * immediate-ui.c - opérandes représentant des valeurs numériques sous forme graphique + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "immediate-ui.h" + + +#include <i18n.h> + + +#include "immediate-int.h" +#include "../../common/extstr.h" +#include "../../glibext/tokenstyle.h" +#include "../../glibext/options/asm.h" + + + +/* Traduit un opérande en version humainement lisible. */ +static void g_immediate_operand_ui_print(const GImmediateOperand *, GBufferLine *); + +/* Construit un petit résumé concis de l'opérande. */ +static char *g_immediate_operand_ui_build_tooltip(const GImmediateOperand *, const GLoadedBinary *); + + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'opérande UI. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_immediate_operand_ui_arch_operand_ui_iface_init(GArchOperandUIInterface *iface) +{ + iface->print = (print_operand_ui_fc)g_immediate_operand_ui_print; + iface->build_tooltip = (build_operand_ui_tooltip_fc)g_immediate_operand_ui_build_tooltip; + +} + + +/****************************************************************************** +* * +* Paramètres : operand = opérande à traiter. * +* line = ligne tampon où imprimer l'opérande donné. * +* * +* Description : Traduit un opérande en version humainement lisible. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_immediate_operand_ui_print(const GImmediateOperand *operand, GBufferLine *line) +{ + GImmediateOperand *base; /* Version d'instance basique */ + ImmOperandDisplay display; /* Type d'affichage courant */ + char value[IMM_MAX_SIZE]; /* Chaîne à imprimer */ + size_t len; /* Taille de l'élément inséré */ + + base = G_IMMEDIATE_OPERAND(operand); + + display = g_immediate_operand_get_display(base); + + len = _g_immediate_operand_to_string(base, display, value); + + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_IMMEDIATE, value, len, NULL, G_OBJECT(operand)); + +} + + +/****************************************************************************** +* * +* Paramètres : operand = opérande à consulter. * +* binary = informations relatives au binaire chargé. * +* * +* Description : Construit un petit résumé concis de l'opérande. * +* * +* Retour : Chaîne de caractères à libérer après usage ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_immediate_operand_ui_build_tooltip(const GImmediateOperand *operand, const GLoadedBinary *binary) +{ + char *result; /* Description à retourner */ + GImmediateOperand *base; /* Version d'instance basique */ + char value[IMM_MAX_SIZE]; /* Conversion artificielle */ + char *conv; /* Affichage de la Conversion */ + + base = G_IMMEDIATE_OPERAND(operand); + + if (base->raw <= UCHAR_MAX && isprint(base->raw)) + switch (base->raw) + { + case '&': + asprintf(&result, _("Character: '&'")); + break; + case '<': + asprintf(&result, _("Character: '<'")); + break; + case '>': + asprintf(&result, _("Character: '>'")); + break; + default: + asprintf(&result, _("Character: '%c'"), (char)base->raw); + break; + } + + else + asprintf(&result, _("Character: <not printable>")); + + /* Binaire */ + + _g_immediate_operand_to_string(base, IOD_BIN, value); + + asprintf(&conv, _("Binary: %s"), value); + + result = stradd(result, "\n"); + result = stradd(result, conv); + + free(conv); + + /* Octal */ + + _g_immediate_operand_to_string(base, IOD_OCT, value); + + asprintf(&conv, _("Octal: %s"), value); + + result = stradd(result, "\n"); + result = stradd(result, conv); + + free(conv); + + /* Décimal */ + + _g_immediate_operand_to_string(base, IOD_DEC, value); + + asprintf(&conv, _("Decimal: %s"), value); + + result = stradd(result, "\n"); + result = stradd(result, conv); + + free(conv); + + /* Hexadécimal */ + + _g_immediate_operand_to_string(base, IOD_HEX, value); + + asprintf(&conv, _("Hexadecimal: %s"), value); + + result = stradd(result, "\n"); + result = stradd(result, conv); + + free(conv); + + return result; + +} diff --git a/src/arch/operands/immediate-ui.h b/src/arch/operands/immediate-ui.h new file mode 100644 index 0000000..4dbddae --- /dev/null +++ b/src/arch/operands/immediate-ui.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * immediate-ui.h - prototypes pour les opérandes représentant des valeurs numériques sous forme graphique + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_OPERANDS_IMMEDIATE_UI_H +#define _ARCH_OPERANDS_IMMEDIATE_UI_H + + +#include "../operand-ui-int.h" + + + +/* Procède à l'initialisation de l'interface d'opérande UI. */ +void g_immediate_operand_ui_arch_operand_ui_iface_init(GArchOperandUIInterface *); + + + +#endif /* _ARCH_OPERANDS_IMMEDIATE_UI_H */ diff --git a/src/arch/operands/immediate.c b/src/arch/operands/immediate.c index f40c645..f99b421 100644 --- a/src/arch/operands/immediate.c +++ b/src/arch/operands/immediate.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * immediate.c - opérandes représentant des valeurs numériques * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,25 +25,32 @@ #include <assert.h> +#include <stdarg.h> + + + +#if 0 + #include <ctype.h> #include <inttypes.h> #include <limits.h> #include <malloc.h> -#include <stdarg.h> #include <stdio.h> #include <i18n.h> +#endif + + #include "immediate-int.h" -#include "known.h" -#include "rename-int.h" -#include "targetable-int.h" #include "../../common/asm.h" -#include "../../common/extstr.h" #include "../../common/sort.h" -#include "../../core/columns.h" +#include "../../glibext/comparable-int.h" +#include "../../glibext/hashable-int.h" +#include "../../glibext/serialize-int.h" +#include "../../glibext/strbuilder-int.h" @@ -51,68 +58,75 @@ /* Initialise la classe des opérandes de valeur immédiate. */ -static void g_imm_operand_class_init(GImmOperandClass *); +static void g_immediate_operand_class_init(GImmediateOperandClass *); -/* Initialise un opérande de valeur immédiate. */ -static void g_imm_operand_init(GImmOperand *); +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_immediate_operand_comparable_object_iface_init(GComparableObjectInterface *); + +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_immediate_operand_hashable_object_iface_init(GHashableObjectInterface *); + +/* Procède à l'initialisation de l'interface de sérialisation. */ +static void g_immediate_operand_serializable_object_iface_init(GSerializableObjectInterface *); + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void g_immediate_operand_string_builder_iface_init(GStringBuilderInterface *, gpointer); + +#if 0 /* Procède à l'initialisation de l'interface de ciblage. */ -static void g_imm_operand_targetable_interface_init(GTargetableOperandInterface *); +static void g_immediate_operand_targetable_interface_init(GTargetableOperandInterface *); /* Procède à l'initialisation de l'interface de renommage. */ -static void g_imm_operand_renameable_interface_init(GRenameableOperandInterface *); +static void g_immediate_operand_renameable_interface_init(GRenameableOperandInterface *); + +#endif + +/* Initialise un opérande de valeur immédiate. */ +static void g_immediate_operand_init(GImmediateOperand *); /* Supprime toutes les références externes. */ -static void g_imm_operand_dispose(GImmOperand *); +static void g_immediate_operand_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_imm_operand_finalize(GImmOperand *); +static void g_immediate_operand_finalize(GObject *); -/* Construit la chaîne de caractères correspondant à l'opérande. */ -static size_t _g_imm_operand_to_string(const GImmOperand *, ImmOperandDisplay, char [IMM_MAX_SIZE]); -/* Traduit un opérande en version humainement lisible. */ -static void g_imm_operand_print(const GImmOperand *, GBufferLine *); +/* ---------------------- COMPARAISON DETAILLEE DE DEUX OBJETS ---------------------- */ -/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ +/* Réalise une comparaison étendue entre objets. */ +static int g_immediate_operand_compare(const GComparableObject *, const GComparableObject *); -/* Compare un opérande avec un autre. */ -static int g_imm_operand_compare(const GImmOperand *, const GImmOperand *, bool); -#ifdef INCLUDE_GTK_SUPPORT +/* ---------------------- CALCUL D'UNE EMPREINTE DE L'INSTANCE ---------------------- */ -/* Construit un petit résumé concis de l'opérande. */ -static char *g_imm_operand_build_tooltip(const GImmOperand *, const GLoadedBinary *); -#endif +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint g_immediate_operand_hash(const GHashableObject *); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint g_imm_operand_hash(const GImmOperand *, bool); -/* Charge un contenu depuis une mémoire tampon. */ -static bool g_imm_operand_load(GImmOperand *, GObjectStorage *, packed_buffer_t *); -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool g_imm_operand_store(GImmOperand *, GObjectStorage *, packed_buffer_t *); +/* ------------------- MECANISMES DE CONSERVATION ET RESTAURATION ------------------- */ +/* Charge un objet depuis un flux de données. */ +static bool g_immediate_operand_load(GSerializableObject *, GObjectStorage *, int); -/* ---------------------- COMMUNICATION D'UN CIBLAGE POTENTIEL ---------------------- */ +/* Sauvegarde un objet dans un flux de données. */ +static bool g_immediate_operand_store(const GSerializableObject *, GObjectStorage *, int); -/* Obtient l'adresse de la cible visée par un opérande. */ -static bool g_imm_operand_get_addr(const GImmOperand *, const vmpa2t *, GBinFormat *, GArchProcessor *, vmpa2t *); +/* ----------------- EXPORTATION SOUS FORME DE CHAINE DE CARACTERES ----------------- */ -/* ---------------------- CONSTRUCTION D'UN CONTENU ALTERNATIF ---------------------- */ +/* Exporte une chaîne de caractères à partir d'un objet. */ +static bool g_immediate_operand_to_string(const GStringBuilder *, unsigned int, sized_binary_t *); -/* Construit un opérande de représentation alternative. */ -static GRenamedOperand *g_imm_operand_build(const GImmOperand *, const char *); @@ -122,9 +136,12 @@ static GRenamedOperand *g_imm_operand_build(const GImmOperand *, const char *); /* Indique le type défini pour un opérande de valeur numérique. */ -G_DEFINE_TYPE_WITH_CODE(GImmOperand, g_imm_operand, G_TYPE_ARCH_OPERAND, - G_IMPLEMENT_INTERFACE(G_TYPE_TARGETABLE_OPERAND, g_imm_operand_targetable_interface_init) - G_IMPLEMENT_INTERFACE(G_TYPE_RENAMEABLE_OPERAND, g_imm_operand_renameable_interface_init)); +G_DEFINE_TYPE_WITH_CODE(GImmediateOperand, g_immediate_operand, G_TYPE_ARCH_OPERAND, + G_IMPLEMENT_INTERFACE(G_TYPE_COMPARABLE_OBJECT, g_immediate_operand_comparable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_HASHABLE_OBJECT, g_immediate_operand_hashable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_immediate_operand_serializable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_STRING_BUILDER, g_immediate_operand_string_builder_iface_init) + G_IMPLEMENT_INTERFACE_IF_SYM(g_arch_operand_ui_get_type, g_immediate_operand_ui_arch_operand_ui_iface_init)); /****************************************************************************** @@ -139,36 +156,42 @@ G_DEFINE_TYPE_WITH_CODE(GImmOperand, g_imm_operand, G_TYPE_ARCH_OPERAND, * * ******************************************************************************/ -static void g_imm_operand_class_init(GImmOperandClass *klass) +static void g_immediate_operand_class_init(GImmediateOperandClass *klass) { GObjectClass *object; /* Autre version de la classe */ - GArchOperandClass *operand; /* Version de classe parente */ object = G_OBJECT_CLASS(klass); - operand = G_ARCH_OPERAND_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_imm_operand_dispose; - object->finalize = (GObjectFinalizeFunc)g_imm_operand_finalize; + object->dispose = g_immediate_operand_dispose; + object->finalize = g_immediate_operand_finalize; - operand->compare = (operand_compare_fc)g_imm_operand_compare; - operand->print = (operand_print_fc)g_imm_operand_print; -#ifdef INCLUDE_GTK_SUPPORT - operand->build_tooltip = (operand_build_tooltip_fc)g_imm_operand_build_tooltip; -#endif +} - operand->hash = (operand_hash_fc)g_imm_operand_hash; - operand->load = (load_operand_fc)g_imm_operand_load; - operand->store = (store_operand_fc)g_imm_operand_store; +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de comparaison. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_immediate_operand_comparable_object_iface_init(GComparableObjectInterface *iface) +{ + iface->compare = g_immediate_operand_compare; } /****************************************************************************** * * -* Paramètres : operand = instance à initialiser. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Initialise un opérande de valeur immédiate. * +* Description : Procède à l'initialisation de l'interface de détermination. * * * * Retour : - * * * @@ -176,23 +199,39 @@ static void g_imm_operand_class_init(GImmOperandClass *klass) * * ******************************************************************************/ -static void g_imm_operand_init(GImmOperand *operand) +static void g_immediate_operand_hashable_object_iface_init(GHashableObjectInterface *iface) { - GET_IMM_OP_EXTRA(operand)->size = MDS_UNDEFINED; + iface->hash = g_immediate_operand_hash; - GET_IMM_OP_EXTRA(operand)->def_display = IOD_HEX; - GET_IMM_OP_EXTRA(operand)->display = IOD_COUNT; +} - operand->raw = 0; + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de sérialisation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_immediate_operand_serializable_object_iface_init(GSerializableObjectInterface *iface) +{ + iface->load = g_immediate_operand_load; + iface->store = g_immediate_operand_store; } /****************************************************************************** * * -* Paramètres : iface = interface GLib à initialiser. * +* Paramètres : iface = interface GLib à initialiser. * +* unused = pointeur non utilisé ici. * * * -* Description : Procède à l'initialisation de l'interface de ciblage. * +* Description : Procède à l'initialisation de l'interface d'exportation. * * * * Retour : - * * * @@ -200,18 +239,18 @@ static void g_imm_operand_init(GImmOperand *operand) * * ******************************************************************************/ -static void g_imm_operand_targetable_interface_init(GTargetableOperandInterface *iface) +static void g_immediate_operand_string_builder_iface_init(GStringBuilderInterface *iface, gpointer unused) { - iface->get_addr = (get_targetable_addr_fc)g_imm_operand_get_addr; + iface->to_string = g_immediate_operand_to_string; } /****************************************************************************** * * -* Paramètres : iface = interface GLib à initialiser. * +* Paramètres : operand = instance à initialiser. * * * -* Description : Procède à l'initialisation de l'interface de renommage. * +* Description : Initialise un opérande de valeur immédiate. * * * * Retour : - * * * @@ -219,16 +258,27 @@ static void g_imm_operand_targetable_interface_init(GTargetableOperandInterface * * ******************************************************************************/ -static void g_imm_operand_renameable_interface_init(GRenameableOperandInterface *iface) +static void g_immediate_operand_init(GImmediateOperand *operand) { - iface->build = (build_renameable_fc)g_imm_operand_build; + immop_extra_data_t extra; /* Données insérées à consulter*/ + + extra = GET_IMM_OP_EXTRA(operand); + + extra.size = MDS_UNDEFINED; + + extra.def_display = IOD_HEX; + extra.display = IOD_COUNT; + + SET_IMM_OP_EXTRA(operand, &extra); + + operand->raw = 0; } /****************************************************************************** * * -* Paramètres : operand = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -238,16 +288,16 @@ static void g_imm_operand_renameable_interface_init(GRenameableOperandInterface * * ******************************************************************************/ -static void g_imm_operand_dispose(GImmOperand *operand) +static void g_immediate_operand_dispose(GObject *object) { - G_OBJECT_CLASS(g_imm_operand_parent_class)->dispose(G_OBJECT(operand)); + G_OBJECT_CLASS(g_immediate_operand_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : operand = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -257,9 +307,74 @@ static void g_imm_operand_dispose(GImmOperand *operand) * * ******************************************************************************/ -static void g_imm_operand_finalize(GImmOperand *operand) +static void g_immediate_operand_finalize(GObject *object) +{ + G_OBJECT_CLASS(g_immediate_operand_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : size = taille de l'opérande souhaitée. * +* value = valeur sur x bits à venir récupérer. * +* * +* Description : Crée un opérande réprésentant une valeur numérique. * +* * +* Retour : Instruction mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GArchOperand *g_immediate_operand_new_from_value(MemoryDataSize size, uint64_t value) { - G_OBJECT_CLASS(g_imm_operand_parent_class)->finalize(G_OBJECT(operand)); + GImmediateOperand *result; /* Opérande à retourner */ + + result = g_object_new(G_TYPE_IMMEDIATE_OPERAND, NULL); + + if (!g_immediate_operand_create_from_value(result, size, value)) + g_clear_object(&result); + + return G_ARCH_OPERAND(result); + +} + + +/****************************************************************************** +* * +* Paramètres : operand = instance à initialiser pleinement. * +* size = taille de l'opérande souhaitée. * +* value = valeur sur x bits à venir récupérer. * +* * +* Description : Met en place un opérande réprésentant une valeur numérique. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_immediate_operand_create_from_value(GImmediateOperand *operand, MemoryDataSize size, uint64_t value) +{ + bool result; /* Bilan à retourner */ + immop_extra_data_t extra; /* Données insérées à consulter*/ + + result = (size != MDS_UNDEFINED); + + if (result) + { + extra = GET_IMM_OP_EXTRA(operand); + + extra.size = size; + + operand->raw = value; + + SET_IMM_OP_EXTRA(operand, &extra); + + } + + return result; } @@ -280,10 +395,41 @@ static void g_imm_operand_finalize(GImmOperand *operand) * * ******************************************************************************/ -GArchOperand *_g_imm_operand_new_from_data(MemoryDataSize size, const GBinContent *content, vmpa2t *addr, bool *low, SourceEndian endian) +GArchOperand *g_immediate_operand_new_from_data(MemoryDataSize size, const GBinContent *content, vmpa2t *addr, bool *low, SourceEndian endian) { - GImmOperand *result; /* Opérande à retourner */ - immop_extra_data_t *extra; /* Données insérées à modifier */ + GImmediateOperand *result; /* Opérande à retourner */ + + result = g_object_new(G_TYPE_IMMEDIATE_OPERAND, NULL); + + if (!g_immediate_operand_create_from_data(result, size, content, addr, low, endian)) + g_clear_object(&result); + + return G_ARCH_OPERAND(result); + +} + + +/****************************************************************************** +* * +* Paramètres : operand = instance à initialiser pleinement. * +* size = taille de l'opérande souhaitée. * +* content = flux de données à analyser. * +* addr = position courante dans ce flux. [OUT] * +* low = position éventuelle des 4 bits visés. [OUT] * +* endian = ordre des bits dans la source. * +* * +* Description : Crée un opérande réprésentant une valeur numérique. * +* * +* Retour : Instruction mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_immediate_operand_create_from_data(GImmediateOperand *operand, MemoryDataSize size, const GBinContent *content, vmpa2t *addr, bool *low, SourceEndian endian) +{ + bool result; /* Bilan à retourner */ + uint64_t raw; /* Valeur brute lue */ uint8_t uval8; /* Valeur sur 8 bits */ uint16_t uval16; /* Valeur sur 16 bits */ uint32_t uval32; /* Valeur sur 32 bits */ @@ -293,125 +439,78 @@ GArchOperand *_g_imm_operand_new_from_data(MemoryDataSize size, const GBinConten int32_t sval32; /* Valeur sur 32 bits */ int64_t sval64; /* Valeur sur 64 bits */ - result = g_object_new(G_TYPE_IMM_OPERAND, NULL); - - extra = GET_IMM_OP_EXTRA(result); - - extra->size = size; - switch (size) { case MDS_4_BITS_UNSIGNED: - if (!g_binary_content_read_u4(content, addr, low, &uval8)) - goto gionfd_error; - result->raw = uval8; + result = g_binary_content_read_u4(content, addr, low, &uval8); + if (result) + raw = uval8; break; case MDS_8_BITS_UNSIGNED: - if (!g_binary_content_read_u8(content, addr, &uval8)) - goto gionfd_error; - result->raw = uval8; + result = g_binary_content_read_u8(content, addr, &uval8); + if (result) + raw = uval8; break; case MDS_16_BITS_UNSIGNED: - if (!g_binary_content_read_u16(content, addr, endian, &uval16)) - goto gionfd_error; - result->raw = uval16; + result = g_binary_content_read_u16(content, addr, endian, &uval16); + if (result) + raw = uval16; break; case MDS_32_BITS_UNSIGNED: - if (!g_binary_content_read_u32(content, addr, endian, &uval32)) - goto gionfd_error; - result->raw = uval32; + result = g_binary_content_read_u32(content, addr, endian, &uval32); + if (result) + raw = uval32; break; case MDS_64_BITS_UNSIGNED: - if (!g_binary_content_read_u64(content, addr, endian, &uval64)) - goto gionfd_error; - result->raw = uval64; + result = g_binary_content_read_u64(content, addr, endian, &uval64); + if (result) + raw = uval64; break; case MDS_4_BITS_SIGNED: - if (!g_binary_content_read_s4(content, addr, low, &sval8)) - goto gionfd_error; - result->raw = sval8; + result = g_binary_content_read_s4(content, addr, low, &sval8); + if (result) + raw = sval8; break; case MDS_8_BITS_SIGNED: - if (!g_binary_content_read_s8(content, addr, &sval8)) - goto gionfd_error; - result->raw = sval8; + result = g_binary_content_read_s8(content, addr, &sval8); + if (result) + raw = sval8; break; case MDS_16_BITS_SIGNED: - if (!g_binary_content_read_s16(content, addr, endian, &sval16)) - goto gionfd_error; - result->raw = sval16; + result = g_binary_content_read_s16(content, addr, endian, &sval16); + if (result) + raw = sval16; break; case MDS_32_BITS_SIGNED: - if (!g_binary_content_read_s32(content, addr, endian, &sval32)) - goto gionfd_error; - result->raw = sval32; + result = g_binary_content_read_s32(content, addr, endian, &sval32); + if (result) + raw = sval32; break; case MDS_64_BITS_SIGNED: - if (!g_binary_content_read_s64(content, addr, endian, &sval64)) - goto gionfd_error; - result->raw = sval64; + result = g_binary_content_read_s64(content, addr, endian, &sval64); + if (result) + raw = sval64; break; case MDS_UNDEFINED: - goto gionfd_error; + result = false; break; } - return G_ARCH_OPERAND(result); - - gionfd_error: - - g_object_unref(G_OBJECT(result)); - - return NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : size = taille de l'opérande souhaitée. * -* value = valeur sur x bits à venir récupérer. * -* * -* Description : Crée un opérande réprésentant une valeur numérique. * -* * -* Retour : Instruction mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GArchOperand *g_imm_operand_new_from_value(MemoryDataSize size, uint64_t value) -{ - GImmOperand *result; /* Opérande à retourner */ - immop_extra_data_t *extra; /* Données insérées à modifier */ - - if (size == MDS_UNDEFINED) - result = NULL; - - else - { - result = g_object_new(G_TYPE_IMM_OPERAND, NULL); - - extra = GET_IMM_OP_EXTRA(result); - - extra->size = size; - - result->raw = value; - - } + if (result) + result = g_immediate_operand_create_from_value(operand, size, raw); - return (result != NULL ? G_ARCH_OPERAND(result) : NULL); + return result; } @@ -428,18 +527,14 @@ GArchOperand *g_imm_operand_new_from_value(MemoryDataSize size, uint64_t value) * * ******************************************************************************/ -MemoryDataSize g_imm_operand_get_size(const GImmOperand *operand) +MemoryDataSize g_immediate_operand_get_size(const GImmediateOperand *operand) { MemoryDataSize result; /* Taille à retourner */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ + immop_extra_data_t extra; /* Données insérées à consulter*/ extra = GET_IMM_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); - - result = extra->size; - - UNLOCK_GOBJECT_EXTRA(extra); + result = extra.size; return result; @@ -460,10 +555,10 @@ MemoryDataSize g_imm_operand_get_size(const GImmOperand *operand) * * ******************************************************************************/ -bool g_imm_operand_get_value(const GImmOperand *operand, MemoryDataSize size, ...) +bool g_immediate_operand_get_value(const GImmediateOperand *operand, MemoryDataSize size, ...) { bool result; /* Bilan à retourner */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ + immop_extra_data_t extra; /* Données insérées à consulter*/ va_list ap; /* Liste des compléments */ uint8_t *uval8; /* Valeur sur 8 bits */ uint16_t *uval16; /* Valeur sur 16 bits */ @@ -478,9 +573,7 @@ bool g_imm_operand_get_value(const GImmOperand *operand, MemoryDataSize size, .. extra = GET_IMM_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); - - if (extra->size != size) + if (extra.size != size) goto exit; va_start(ap, size); @@ -533,8 +626,6 @@ bool g_imm_operand_get_value(const GImmOperand *operand, MemoryDataSize size, .. exit: - UNLOCK_GOBJECT_EXTRA(extra); - return result; } @@ -542,6 +633,37 @@ bool g_imm_operand_get_value(const GImmOperand *operand, MemoryDataSize size, .. /****************************************************************************** * * +* Paramètres : operand = structure dont le contenu est à actualiser. [OUT] * +* size = taille de l'opérande souhaitée. * +* value = valeur sur x bits à venir récupérer. * +* * +* Description : Définit la nouvelle valeur de l'opérande à une valeur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_immediate_operand_set_value(GImmediateOperand *operand, MemoryDataSize size, uint64_t value) +{ + immop_extra_data_t extra; /* Données insérées à consulter*/ + + assert(size != MDS_UNDEFINED); + + extra = GET_IMM_OP_EXTRA(operand); + + extra.size = size; + + operand->raw = value; + + SET_IMM_OP_EXTRA(operand, &extra); + +} + + +/****************************************************************************** +* * * Paramètres : operand = opérande à consulter. * * * * Description : Fournit la valeur brute représentée par l'opérande. * @@ -552,42 +674,55 @@ bool g_imm_operand_get_value(const GImmOperand *operand, MemoryDataSize size, .. * * ******************************************************************************/ -uint64_t g_imm_operand_get_raw_value(const GImmOperand *operand) +uint64_t g_immediate_operand_get_raw_value(const GImmediateOperand *operand) { - return operand->raw; + uint64_t result; /* Valeur brute à retourner */ + + result = operand->raw; + + return result; } /****************************************************************************** * * -* Paramètres : operand = structure dont le contenu est à actualiser. [OUT] * -* size = taille de l'opérande souhaitée. * -* value = valeur sur x bits à venir récupérer. * +* Paramètres : operand = structure dont le contenu est à consulter. * * * -* Description : Définit la nouvelle valeur de l'opérande à une valeur. * +* Description : Indique le signe d'une valeur immédiate. * * * -* Retour : - * +* Retour : true si la valeur est strictement négative, false sinon. * * * * Remarques : - * * * ******************************************************************************/ -void g_imm_operand_set_value(GImmOperand *operand, MemoryDataSize size, uint64_t value) +bool g_immediate_operand_is_negative(const GImmediateOperand *operand) { - immop_extra_data_t *extra; /* Données insérées à consulter*/ - - assert(size != MDS_UNDEFINED); + bool result; /* Bilan à renvoyer */ + immop_extra_data_t extra; /* Données insérées à consulter*/ extra = GET_IMM_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); - - extra->size = size; - - operand->raw = value; + switch (extra.size) + { + case MDS_4_BITS_SIGNED: + case MDS_8_BITS_SIGNED: + case MDS_16_BITS_SIGNED: + case MDS_32_BITS_SIGNED: + case MDS_64_BITS_SIGNED: + /** + * Pour les valeurs plus petites que 64 bits, le compilateur + * réalise une extension de signe lors du transtypage. + */ + result = (operand->raw & 0x8000000000000000ll); + break; + default: + result = false; + break; + } - UNLOCK_GOBJECT_EXTRA(extra); + return result; } @@ -605,17 +740,15 @@ void g_imm_operand_set_value(GImmOperand *operand, MemoryDataSize size, uint64_t * * ******************************************************************************/ -void g_imm_operand_set_default_display(GImmOperand *operand, ImmOperandDisplay display) +void g_immediate_operand_set_default_display(GImmediateOperand *operand, ImmOperandDisplay display) { - immop_extra_data_t *extra; /* Données insérées à consulter*/ + immop_extra_data_t extra; /* Données insérées à consulter*/ extra = GET_IMM_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); + extra.def_display = display; - extra->def_display = display; - - UNLOCK_GOBJECT_EXTRA(extra); + SET_IMM_OP_EXTRA(operand, &extra); } @@ -632,18 +765,14 @@ void g_imm_operand_set_default_display(GImmOperand *operand, ImmOperandDisplay d * * ******************************************************************************/ -ImmOperandDisplay g_imm_operand_get_default_display(const GImmOperand *operand) +ImmOperandDisplay g_immediate_operand_get_default_display(const GImmediateOperand *operand) { ImmOperandDisplay result; /* Affichage à retourner */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ + immop_extra_data_t extra; /* Données insérées à consulter*/ extra = GET_IMM_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); - - result = extra->def_display; - - UNLOCK_GOBJECT_EXTRA(extra); + result = extra.def_display; return result; @@ -663,17 +792,15 @@ ImmOperandDisplay g_imm_operand_get_default_display(const GImmOperand *operand) * * ******************************************************************************/ -void g_imm_operand_set_display(GImmOperand *operand, ImmOperandDisplay display) +void g_immediate_operand_set_display(GImmediateOperand *operand, ImmOperandDisplay display) { - immop_extra_data_t *extra; /* Données insérées à consulter*/ + immop_extra_data_t extra; /* Données insérées à consulter*/ extra = GET_IMM_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); + extra.display = display; - extra->display = display; - - UNLOCK_GOBJECT_EXTRA(extra); + SET_IMM_OP_EXTRA(operand, &extra); } @@ -690,94 +817,225 @@ void g_imm_operand_set_display(GImmOperand *operand, ImmOperandDisplay display) * * ******************************************************************************/ -ImmOperandDisplay g_imm_operand_get_display(const GImmOperand *operand) +ImmOperandDisplay g_immediate_operand_get_display(const GImmediateOperand *operand) { ImmOperandDisplay result; /* Affichage à retourner */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ + immop_extra_data_t extra; /* Données insérées à consulter*/ extra = GET_IMM_OP_EXTRA(operand); - LOCK_GOBJECT_EXTRA(extra); - - if (extra->display != IOD_COUNT) - result = extra->display; + if (extra.display != IOD_COUNT) + result = extra.display; else - result = extra->def_display; - - UNLOCK_GOBJECT_EXTRA(extra); + result = extra.def_display; return result; } + +/* ---------------------------------------------------------------------------------- */ +/* COMPARAISON DETAILLEE DE DEUX OBJETS */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = structure dont le contenu est à consulter. * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * * * -* Description : Indique le signe d'une valeur immédiate. * +* Description : Réalise une comparaison étendue entre objets. * * * -* Retour : true si la valeur est strictement négative, false sinon. * +* Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ -bool g_imm_operand_is_negative(const GImmOperand *operand) +static int g_immediate_operand_compare(const GComparableObject *object, const GComparableObject *other) { - bool result; /* Bilan à renvoyer */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ + int result; /* Bilan à retourner */ + GComparableObjectInterface *iface; /* Interface utilisée */ + GComparableObjectInterface *parent_iface; /* Interface parente */ + GImmediateOperand *operand_a; /* Version spécialisée #0 */ + GImmediateOperand *operand_b; /* Version spécialisée #1 */ + immop_extra_data_t extra_a; /* Données insérées à consulter*/ + immop_extra_data_t extra_b; /* Données insérées à consulter*/ - extra = GET_IMM_OP_EXTRA(operand); + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); - LOCK_GOBJECT_EXTRA(extra); + result = parent_iface->compare(object, other); - switch (extra->size) + if (result == 0) { - case MDS_4_BITS_SIGNED: - case MDS_8_BITS_SIGNED: - case MDS_16_BITS_SIGNED: - case MDS_32_BITS_SIGNED: - case MDS_64_BITS_SIGNED: - /** - * Pour les valeurs plus petites que 64 bits, le compilateur - * réalise une extension de signe lors du transtypage. - */ - result = (operand->raw & 0x8000000000000000ll); - break; - default: - result = false; - break; + operand_a = G_IMMEDIATE_OPERAND(object); + + extra_a = GET_IMM_OP_EXTRA(operand_a); + + operand_b = G_IMMEDIATE_OPERAND(other); + + extra_b = GET_IMM_OP_EXTRA(operand_b); + + result = sort_unsigned_long(extra_a.size, extra_b.size); + + if (result == 0) + sort_uint64_t(operand_a->raw, operand_b->raw); + + if (result == 0) + result = sort_unsigned_long(extra_a.def_display, extra_b.def_display); + + if (result == 0) + result = sort_unsigned_long(extra_a.display, extra_b.display); + } - UNLOCK_GOBJECT_EXTRA(extra); + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CALCUL D'UNE EMPREINTE DE L'INSTANCE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : object = objet dont l'instance est à consulter. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static guint g_immediate_operand_hash(const GHashableObject *object) +{ + guint result; /* Valeur à retourner */ + GHashableObjectInterface *iface; /* Interface utilisée */ + GHashableObjectInterface *parent_iface; /* Interface parente */ + GImmediateOperand *operand; /* Version spécialisée */ + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + result = parent_iface->hash(object); + + operand = G_IMMEDIATE_OPERAND(object); + + result ^= (operand->raw & 0xffffffff); + result ^= (operand->raw >> 32); return result; } + +/* ---------------------------------------------------------------------------------- */ +/* MECANISMES DE CONSERVATION ET RESTAURATION */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = structure dont le contenu est à consulter. * +* Paramètres : object = élément GLib à constuire. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * * * -* Description : Indique si une valeur immédiate est nulle ou non. * +* Description : Charge un objet depuis un flux de données. * * * -* Retour : true si la valeur est nulle, false sinon. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -bool g_imm_operand_is_null(const GImmOperand *operand) +static bool g_immediate_operand_load(GSerializableObject *object, GObjectStorage *storage, int fd) { - return (operand->raw == 0ll); + bool result; /* Bilan à retourner */ + GSerializableObjectInterface *iface; /* Interface utilisée */ + GSerializableObjectInterface *parent_iface; /* Interface parente */ + uleb128_t val; /* Valeur sauvegardée */ + GImmediateOperand *operand; /* Version spécialisée */ + + iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + result = parent_iface->load(object, storage, fd); + + if (result) + { + result = load_uleb128(&val, fd); + + if (result) + { + operand = G_IMMEDIATE_OPERAND(object); + operand->raw = val; + } + + } + + return result; } /****************************************************************************** * * +* Paramètres : object = élément GLib à consulter. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * +* * +* Description : Sauvegarde un objet dans un flux de données. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_immediate_operand_store(const GSerializableObject *object, GObjectStorage *storage, int fd) +{ + bool result; /* Bilan à retourner */ + GSerializableObjectInterface *iface; /* Interface utilisée */ + GSerializableObjectInterface *parent_iface; /* Interface parente */ + GImmediateOperand *operand; /* Version spécialisée */ + + iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + result = parent_iface->store(object, storage, fd); + if (!result) goto exit; + + operand = G_IMMEDIATE_OPERAND(object); + + result = store_uleb128((uleb128_t []) { operand->raw }, fd); + + exit: + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* EXPORTATION SOUS FORME DE CHAINE DE CARACTERES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * * Paramètres : operand = opérande à transcrire. * * display = type d'affichage demandé. * * value = valeur portée par l'opérande transcrite. [OUT] * @@ -790,10 +1048,10 @@ bool g_imm_operand_is_null(const GImmOperand *operand) * * ******************************************************************************/ -static size_t _g_imm_operand_to_string(const GImmOperand *operand, ImmOperandDisplay display, char value[IMM_MAX_SIZE]) +size_t _g_immediate_operand_to_string(const GImmediateOperand *operand, ImmOperandDisplay display, char value[IMM_MAX_SIZE]) { size_t result; /* Longueur à retourner */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ + immop_extra_data_t extra; /* Données insérées à consulter*/ unsigned int range; /* Catégorie de la taille */ const char *prefix; /* Entrée en matière */ const char *suffix; /* Sortie de matière */ @@ -813,13 +1071,11 @@ static size_t _g_imm_operand_to_string(const GImmOperand *operand, ImmOperandDis static const char *conv_si_defs[] = { "", "o", "d", "x", "c" }; static const char *conv_us_defs[] = { "", "o", "u", "x", "c" }; - assert(display <= IOD_LAST_VALID); + assert(display < IOD_COUNT); extra = GET_IMM_OP_EXTRA(operand); - //LOCK_GOBJECT_EXTRA(extra); - - range = MDS_RANGE(extra->size); + range = MDS_RANGE(extra.size); /* Encadrement pour les caractères */ if (display == IOD_CHAR) @@ -862,10 +1118,10 @@ static size_t _g_imm_operand_to_string(const GImmOperand *operand, ImmOperandDis if (do_padding) { - if (extra->display != IOD_COUNT) - do_padding = (extra->display != IOD_BIN && extra->display != IOD_HEX); + if (extra.display != IOD_COUNT) + do_padding = (extra.display == IOD_BIN || extra.display == IOD_HEX); else - do_padding = (extra->def_display != IOD_BIN && extra->def_display != IOD_HEX); + do_padding = (extra.def_display == IOD_BIN || extra.def_display == IOD_HEX); } switch (display) @@ -892,7 +1148,7 @@ static size_t _g_imm_operand_to_string(const GImmOperand *operand, ImmOperandDis if (display != IOD_BIN) { - if (MDS_IS_SIGNED(extra->size)) + if (MDS_IS_SIGNED(extra.size)) conv = conv_si_defs[display]; else conv = conv_us_defs[display]; @@ -929,7 +1185,7 @@ static size_t _g_imm_operand_to_string(const GImmOperand *operand, ImmOperandDis snprintf(format, sizeof(format), "%s%s%s%s%s%s%s", prefix, alternate, intro, zpad, lmod, conv, suffix); - switch (extra->size) + switch (extra.size) { case MDS_UNDEFINED: result = snprintf(value, IMM_MAX_SIZE, "<? undef value ?>"); @@ -982,8 +1238,6 @@ static size_t _g_imm_operand_to_string(const GImmOperand *operand, ImmOperandDis } - //UNLOCK_GOBJECT_EXTRA(extra); - assert(result > 0); return result; @@ -993,555 +1247,37 @@ static size_t _g_imm_operand_to_string(const GImmOperand *operand, ImmOperandDis /****************************************************************************** * * -* Paramètres : operand = opérande à transcrire. * -* syntax = type de représentation demandée. * -* value = valeur portée par l'opérande transcrite. [OUT] * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * * * -* Description : Construit la chaîne de caractères correspondant à l'opérande.* +* Description : Exporte une chaîne de caractères à partir d'un objet. * * * -* Retour : Nombre de caractères utilisés. * +* Retour : Bilan de l'opération. * * * -* Remarques : - * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * * * ******************************************************************************/ -size_t g_imm_operand_to_string(const GImmOperand *operand, char value[IMM_MAX_SIZE]) +static bool g_immediate_operand_to_string(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) { - size_t result; /* Longueur à retourner */ + bool result; /* Bilan à retourner */ + const GImmediateOperand *operand; /* Version spécialisée */ ImmOperandDisplay display; /* Type d'affichage courant */ - - display = g_imm_operand_get_display(operand); - - result = _g_imm_operand_to_string(operand, display, value); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à traiter. * -* line = ligne tampon où imprimer l'opérande donné. * -* * -* Description : Traduit un opérande en version humainement lisible. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_imm_operand_print(const GImmOperand *operand, GBufferLine *line) -{ char value[IMM_MAX_SIZE]; /* Chaîne à imprimer */ size_t len; /* Taille de l'élément inséré */ - len = g_imm_operand_to_string(operand, value); - - g_buffer_line_append_text(line, DLC_ASSEMBLY, value, len, RTT_IMMEDIATE, G_OBJECT(operand)); - -} - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à traiter. * -* pos = valeur résultante. [OUT] * -* * -* Description : Convertit une valeur immédiate en position de type phys_t. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_imm_operand_to_phys_t(const GImmOperand *operand, phys_t *pos) -{ - bool result; /* Bilan à renvoyer */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ - - extra = GET_IMM_OP_EXTRA(operand); - - LOCK_GOBJECT_EXTRA(extra); - - result = !MDS_IS_SIGNED(extra->size); - - if (result) - *pos = operand->raw; - - UNLOCK_GOBJECT_EXTRA(extra); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à traiter. * -* addr = valeur résultante. [OUT] * -* * -* Description : Convertit une valeur immédiate en adresse de type virt_t. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_imm_operand_to_virt_t(const GImmOperand *operand, virt_t *addr) -{ - bool result; /* Bilan à renvoyer */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ - - extra = GET_IMM_OP_EXTRA(operand); - - LOCK_GOBJECT_EXTRA(extra); - - result = !MDS_IS_SIGNED(extra->size); - - if (result) - *addr = operand->raw; - - UNLOCK_GOBJECT_EXTRA(extra); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à traiter. * -* val = valeur résultante. [OUT] * -* * -* Description : Convertit une valeur immédiate en valeur de type leb128_t. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_imm_operand_as_leb128(const GImmOperand *operand, leb128_t *val) -{ - immop_extra_data_t *extra; /* Données insérées à consulter*/ - - extra = GET_IMM_OP_EXTRA(operand); - - LOCK_GOBJECT_EXTRA(extra); - - *val = operand->raw; - - UNLOCK_GOBJECT_EXTRA(extra); - -} - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à traiter. * -* val = valeur résultante. [OUT] * -* * -* Description : Convertit une valeur immédiate en valeur de type uleb128_t. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_imm_operand_as_uleb128(const GImmOperand *operand, uleb128_t *val) -{ - immop_extra_data_t *extra; /* Données insérées à consulter*/ - - extra = GET_IMM_OP_EXTRA(operand); - - LOCK_GOBJECT_EXTRA(extra); - - *val = operand->raw; - - UNLOCK_GOBJECT_EXTRA(extra); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : a = premier opérande à consulter. * -* b = second opérande à consulter. * -* lock = précise le besoin en verrouillage. * -* * -* Description : Compare un opérande avec un autre. * -* * -* Retour : Bilan de la comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static int g_imm_operand_compare(const GImmOperand *a, const GImmOperand *b, bool lock) -{ - int result; /* Bilan à retourner */ - immop_extra_data_t *ea; /* Données insérées à modifier */ - immop_extra_data_t *eb; /* Données insérées à modifier */ - GArchOperandClass *class; /* Classe parente normalisée */ - - ea = GET_IMM_OP_EXTRA(a); - eb = GET_IMM_OP_EXTRA(b); - - if (lock) - { - LOCK_GOBJECT_EXTRA(ea); - LOCK_GOBJECT_EXTRA(eb); - } - - result = sort_unsigned_long(ea->size, eb->size); - - if (result == 0) - sort_uint64_t(a->raw, b->raw); - - if (result == 0) - result = sort_unsigned_long(ea->def_display, eb->def_display); - - if (result == 0) - result = sort_unsigned_long(ea->display, eb->display); - - if (result == 0) - { - class = G_ARCH_OPERAND_CLASS(g_imm_operand_parent_class); - result = class->compare(G_ARCH_OPERAND(a), G_ARCH_OPERAND(b), false); - } - - if (lock) - { - UNLOCK_GOBJECT_EXTRA(eb); - UNLOCK_GOBJECT_EXTRA(ea); - } - - return result; - -} - - -#ifdef INCLUDE_GTK_SUPPORT - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à consulter. * -* binary = informations relatives au binaire chargé. * -* * -* Description : Construit un petit résumé concis de l'opérande. * -* * -* Retour : Chaîne de caractères à libérer après usage ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *g_imm_operand_build_tooltip(const GImmOperand *operand, const GLoadedBinary *binary) -{ - char *result; /* Description à retourner */ - char value[IMM_MAX_SIZE]; /* Conversion artificielle */ - char *conv; /* Affichage de la Conversion */ - - if (operand->raw <= UCHAR_MAX && isprint(operand->raw)) - switch (operand->raw) - { - case '&': - asprintf(&result, _("Character: '&'")); - break; - case '<': - asprintf(&result, _("Character: '<'")); - break; - case '>': - asprintf(&result, _("Character: '>'")); - break; - default: - asprintf(&result, _("Character: '%c'"), (char)operand->raw); - break; - } - - else - asprintf(&result, _("Character: <not printable>")); - - /* Binaire */ - - _g_imm_operand_to_string(operand, IOD_BIN, value); - - asprintf(&conv, _("Binary: %s"), value); - - result = stradd(result, "\n"); - result = stradd(result, conv); - - free(conv); - - /* Octal */ - - _g_imm_operand_to_string(operand, IOD_OCT, value); - - asprintf(&conv, _("Octal: %s"), value); - - result = stradd(result, "\n"); - result = stradd(result, conv); - - free(conv); - - /* Décimal */ - - _g_imm_operand_to_string(operand, IOD_DEC, value); - - asprintf(&conv, _("Decimal: %s"), value); - - result = stradd(result, "\n"); - result = stradd(result, conv); - - free(conv); - - /* Hexadécimal */ - - _g_imm_operand_to_string(operand, IOD_HEX, value); - - asprintf(&conv, _("Hexadecimal: %s"), value); - - result = stradd(result, "\n"); - result = stradd(result, conv); - - free(conv); - - return result; - -} - - -#endif - - -/****************************************************************************** -* * -* Paramètres : operand = objet dont l'instance se veut unique. * -* lock = précise le besoin en verrouillage. * -* * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * -* * -* Retour : Empreinte de l'élément représenté. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static guint g_imm_operand_hash(const GImmOperand *operand, bool lock) -{ - guint result; /* Valeur à retourner */ - immop_extra_data_t *extra; /* Données insérées à modifier */ - GArchOperandClass *class; /* Classe parente normalisée */ - - extra = GET_IMM_OP_EXTRA(operand); - - if (lock) - LOCK_GOBJECT_EXTRA(extra); - - class = G_ARCH_OPERAND_CLASS(g_imm_operand_parent_class); - result = class->hash(G_ARCH_OPERAND(operand), false); - - result ^= (operand->raw & 0xffffffff); - result ^= (operand->raw >> 32); - - if (lock) - UNLOCK_GOBJECT_EXTRA(extra); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : operand = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * -* * -* Description : Charge un contenu depuis une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_imm_operand_load(GImmOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - GArchOperandClass *parent; /* Classe parente à consulter */ - immop_extra_data_t *extra; /* Données insérées à modifier */ - uleb128_t value; /* Valeur ULEB128 à charger */ - uint8_t val; /* Champ de bits manipulé */ - - parent = G_ARCH_OPERAND_CLASS(g_imm_operand_parent_class); - - result = parent->load(G_ARCH_OPERAND(operand), storage, pbuf); - - if (result) - { - extra = GET_IMM_OP_EXTRA(operand); - - LOCK_GOBJECT_EXTRA(extra); - - result = unpack_uleb128(&value, pbuf); - - if (result) - extra->size = value; - - if (result) - { - result = extract_packed_buffer(pbuf, &val, sizeof(uint8_t), false); - - if (result) - extra->def_display = val; - - } - - if (result) - { - result = extract_packed_buffer(pbuf, &val, sizeof(uint8_t), false); - - if (result) - extra->display = val; - - } - - UNLOCK_GOBJECT_EXTRA(extra); - - } - - if (result) - result = extract_packed_buffer(pbuf, &operand->raw, sizeof(uint64_t), true); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : operand = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * -* * -* Description : Sauvegarde un contenu dans une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_imm_operand_store(GImmOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - GArchOperandClass *parent; /* Classe parente à consulter */ - immop_extra_data_t *extra; /* Données insérées à modifier */ + operand = G_IMMEDIATE_OPERAND(builder); - parent = G_ARCH_OPERAND_CLASS(g_imm_operand_parent_class); + display = g_immediate_operand_get_display(operand); - result = parent->store(G_ARCH_OPERAND(operand), storage, pbuf); + len = _g_immediate_operand_to_string(operand, display, value); - if (result) - { - extra = GET_IMM_OP_EXTRA(operand); - - LOCK_GOBJECT_EXTRA(extra); - - result = pack_uleb128((uleb128_t []){ extra->size }, pbuf); - - if (result) - result = extend_packed_buffer(pbuf, (uint8_t []) { extra->def_display }, sizeof(uint8_t), false); - - if (result) - result = extend_packed_buffer(pbuf, (uint8_t []) { extra->display }, sizeof(uint8_t), false); - - UNLOCK_GOBJECT_EXTRA(extra); - - } + result = (len > 0); if (result) - result = extend_packed_buffer(pbuf, &operand->raw, sizeof(uint64_t), true); - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* COMMUNICATION D'UN CIBLAGE POTENTIEL */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : operand = operande à consulter. * -* src = localisation de l'instruction mère. * -* format = format reconnu pour le binaire chargé. * -* proc = architecture associée à ce même binaire. * -* addr = localisation de la cible. [OUT] * -* * -* Description : Obtient l'adresse de la cible visée par un opérande. * -* * -* Retour : true si la cible est valide, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_imm_operand_get_addr(const GImmOperand *operand, const vmpa2t *src, GBinFormat *format, GArchProcessor *proc, vmpa2t *addr) -{ - bool result; /* Bilan à retourner */ - virt_t virt; /* Adresse virtuelle */ - - result = g_imm_operand_to_virt_t(operand, &virt); - - if (result) - result = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), virt, addr); - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* CONSTRUCTION D'UN CONTENU ALTERNATIF */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : operand = operande à consulter. * -* text = texte alternatif de représentation. * -* * -* Description : Construit un opérande de représentation alternative. * -* * -* Retour : Nouvel opérande, en version renommée. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GRenamedOperand *g_imm_operand_build(const GImmOperand *operand, const char *text) -{ - GRenamedOperand *result; /* Instance à retourner */ - - result = G_RENAMED_OPERAND(g_known_imm_operand_new(operand, text)); + add_to_sized_binary(out, value, len); return result; diff --git a/src/arch/operands/immediate.h b/src/arch/operands/immediate.h index 7c1ff03..d66349a 100644 --- a/src/arch/operands/immediate.h +++ b/src/arch/operands/immediate.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * immediate.h - prototypes pour les opérandes représentant des valeurs numériques * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,17 +25,22 @@ #define _ARCH_OPERANDS_IMMEDIATE_H -#include <glib-object.h> #include <stdbool.h> #include <stdint.h> -#include "../archbase.h" #include "../operand.h" #include "../../analysis/content.h" +#include "../../common/datatypes.h" +#include "../../glibext/helpers.h" +#define G_TYPE_IMMEDIATE_OPERAND (g_immediate_operand_get_type()) + +DECLARE_GTYPE(GImmediateOperand, g_immediate_operand, G, IMMEDIATE_OPERAND); + + /* Etats particuliers d'un opérande de valeur immédiate */ typedef enum _ImmOpFlag { @@ -57,86 +62,39 @@ typedef enum _ImmOperandDisplay } ImmOperandDisplay; -#define IOD_LAST_VALID IOD_CHAR - - -#define G_TYPE_IMM_OPERAND g_imm_operand_get_type() -#define G_IMM_OPERAND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_IMM_OPERAND, GImmOperand)) -#define G_IS_IMM_OPERAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_IMM_OPERAND)) -#define G_IMM_OPERAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_IMM_OPERAND, GImmOperandClass)) -#define G_IS_IMM_OPERAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_IMM_OPERAND)) -#define G_IMM_OPERAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_IMM_OPERAND, GImmOperandClass)) - - -/* Définition d'un opérande de valeur numérique (instance) */ -typedef struct _GImmOperand GImmOperand; - -/* Définition d'un opérande de valeur numérique (classe) */ -typedef struct _GImmOperandClass GImmOperandClass; - - -/* Indique le type défini pour un opérande d'architecture. */ -GType g_imm_operand_get_type(void); /* Crée un opérande réprésentant une valeur numérique. */ -GArchOperand *_g_imm_operand_new_from_data(MemoryDataSize, const GBinContent *, vmpa2t *, bool *, SourceEndian); - -#define g_imm_operand_new_from_data(size, content, addr, endian) \ - _g_imm_operand_new_from_data(size, content, addr, NULL, endian) +GArchOperand *g_immediate_operand_new_from_value(MemoryDataSize, uint64_t); /* Crée un opérande réprésentant une valeur numérique. */ -GArchOperand *g_imm_operand_new_from_value(MemoryDataSize, uint64_t); +GArchOperand *g_immediate_operand_new_from_data(MemoryDataSize, const GBinContent *, vmpa2t *, bool *, SourceEndian); /* Renseigne la taille de la valeur indiquée à la construction. */ -MemoryDataSize g_imm_operand_get_size(const GImmOperand *); +MemoryDataSize g_immediate_operand_get_size(const GImmediateOperand *); /* Fournit la valeur portée par une opérande numérique. */ -bool g_imm_operand_get_value(const GImmOperand *, MemoryDataSize, ...); +bool g_immediate_operand_get_value(const GImmediateOperand *, MemoryDataSize, ...); + +/* Définit la nouvelle valeur de l'opérande à une valeur. */ +void g_immediate_operand_set_value(GImmediateOperand *, MemoryDataSize, uint64_t); /* Fournit la valeur brute représentée par l'opérande. */ -uint64_t g_imm_operand_get_raw_value(const GImmOperand *); +uint64_t g_immediate_operand_get_raw_value(const GImmediateOperand *); -/* Définit la nouvelle valeur de l'opérande à une valeur. */ -void g_imm_operand_set_value(GImmOperand *, MemoryDataSize, uint64_t); +/* Indique le signe d'une valeur immédiate. */ +bool g_immediate_operand_is_negative(const GImmediateOperand *); /* Définit le format textuel par défaut de la valeur. */ -void g_imm_operand_set_default_display(GImmOperand *, ImmOperandDisplay); +void g_immediate_operand_set_default_display(GImmediateOperand *, ImmOperandDisplay); /* Indique le format textuel par défaut de la valeur. */ -ImmOperandDisplay g_imm_operand_get_default_display(const GImmOperand *); +ImmOperandDisplay g_immediate_operand_get_default_display(const GImmediateOperand *); /* Définit la grande ligne du format textuel de la valeur. */ -void g_imm_operand_set_display(GImmOperand *, ImmOperandDisplay); +void g_immediate_operand_set_display(GImmediateOperand *, ImmOperandDisplay); /* Indique la grande ligne du format textuel de la valeur. */ -ImmOperandDisplay g_imm_operand_get_display(const GImmOperand *); - -/* Indique le signe d'une valeur immédiate. */ -bool g_imm_operand_is_negative(const GImmOperand *); - -/* Indique si une valeur immédiate est nulle ou non. */ -bool g_imm_operand_is_null(const GImmOperand *); - -/** - * La taille d'impression d'un opérande n'est pas VMPA_MAX_SIZE, - * mais 1 + 64 caractères + octet nul final en cas d'impression en binaire. - */ -#define IMM_MAX_SIZE 66 - -/* Construit la chaîne de caractères correspondant à l'opérande. */ -size_t g_imm_operand_to_string(const GImmOperand *, char [IMM_MAX_SIZE]); - -/* Convertit une valeur immédiate en position de type phys_t. */ -bool g_imm_operand_to_phys_t(const GImmOperand *, phys_t *); - -/* Convertit une valeur immédiate en adresse de type virt_t. */ -bool g_imm_operand_to_virt_t(const GImmOperand *, virt_t *); - -/* Convertit une valeur immédiate en valeur de type leb128_t. */ -void g_imm_operand_as_leb128(const GImmOperand *, leb128_t *); - -/* Convertit une valeur immédiate en valeur de type uleb128_t. */ -void g_imm_operand_as_uleb128(const GImmOperand *, uleb128_t *); +ImmOperandDisplay g_immediate_operand_get_display(const GImmediateOperand *); diff --git a/src/arch/operands/known-int.h b/src/arch/operands/known-int.h new file mode 100644 index 0000000..021fbf2 --- /dev/null +++ b/src/arch/operands/known-int.h @@ -0,0 +1,55 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * known-int.h - définitions internes pour les opérandes représentant des valeurs numériques avec sémantique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_OPERANDS_KNOWN_INT_H +#define _ARCH_OPERANDS_KNOWN_INT_H + + +#include "immediate-int.h" +#include "known.h" + + + +/* Définition d'un remplacement d'opérande de valeur numérique (instance) */ +struct _GKnownImmediateOperand +{ + GImmediateOperand parent; /* Instance parente */ + + char *alt_text; /* Alternative humaine */ + +}; + +/* Définition d'un remplacement d'opérande de valeur numérique (classe) */ +struct _GKnownImmediateOperandClass +{ + GImmediateOperandClass parent; /* Classe parente */ + +}; + + +/* Met en place un opérande remplaçant visuellement une valeur. */ +bool g_known_immediate_operand_create(GKnownImmediateOperand *, const GImmediateOperand *, const char *); + + + +#endif /* _ARCH_OPERANDS_KNOWN_INT_H */ diff --git a/src/arch/operands/known-ui.c b/src/arch/operands/known-ui.c new file mode 100644 index 0000000..9b7507a --- /dev/null +++ b/src/arch/operands/known-ui.c @@ -0,0 +1,79 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * known-ui.c - opérandes représentant des valeurs numériques avec sémantique sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "known-ui.h" + + +#include "known-int.h" +#include "../../common/cpp.h" +#include "../../glibext/options/asm.h" + + + +/* Traduit un opérande en version humainement lisible. */ +static void g_known_immediate_operand_ui_print(const GArchOperandUI *, GBufferLine *); + + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'opérande UI. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_known_immediate_operand_ui_arch_operand_ui_iface_init(GArchOperandUIInterface *iface) +{ + iface->print = g_known_immediate_operand_ui_print; + iface->build_tooltip = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : operand = opérande à traiter. * +* line = ligne tampon où imprimer l'opérande donné. * +* * +* Description : Traduit un opérande en version humainement lisible. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_known_immediate_operand_ui_print(const GArchOperandUI *operand, GBufferLine *line) +{ + GKnownImmediateOperand *known; /* Version de base */ + + known = G_KNOWN_IMMEDIATE_OPERAND(operand); + + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_IMMEDIATE, SL(known->alt_text), NULL, G_OBJECT(operand)); + +} diff --git a/src/arch/operands/known-ui.h b/src/arch/operands/known-ui.h new file mode 100644 index 0000000..fa2dc62 --- /dev/null +++ b/src/arch/operands/known-ui.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * known-ui.h - prototypes pour les opérandes représentant des valeurs numériques avec sémantique sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_OPERANDS_KNOWN_UI_H +#define _ARCH_OPERANDS_KNOWN_UI_H + + +#include "../operand-ui-int.h" + + + +/* Procède à l'initialisation de l'interface d'opérande UI. */ +void g_known_immediate_operand_ui_arch_operand_ui_iface_init(GArchOperandUIInterface *); + + + +#endif /* _ARCH_OPERANDS_KNOWN_UI_H */ diff --git a/src/arch/operands/known.c b/src/arch/operands/known.c index 5402879..c9e177f 100644 --- a/src/arch/operands/known.c +++ b/src/arch/operands/known.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * known.c - opérandes représentant des valeurs numériques avec sémantique * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -24,81 +24,80 @@ #include "known.h" -#include <assert.h> #include <malloc.h> #include <string.h> -#include "immediate-int.h" -#include "rename-int.h" -#include "../../analysis/db/misc/rlestr.h" -#include "../../core/columns.h" -#include "../../core/logs.h" +#include "known-int.h" +#include "../../common/cpp.h" +#include "../../glibext/comparable-int.h" +#include "../../glibext/hashable-int.h" +#include "../../glibext/serialize-int.h" +#include "../../glibext/strbuilder-int.h" /* ----------------------- REMPLACEMENT DE VALEURS IMMEDIATES ----------------------- */ -/* Définition d'un remplacement d'opérande de valeur numérique (instance) */ -struct _GKnownImmOperand -{ - GImmOperand parent; /* Instance parente */ +/* Initialise la classe des remplacements d'opérandes. */ +static void g_known_immediate_operand_class_init(GKnownImmediateOperandClass *); - char *alt_text; /* Alternative humaine */ +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_known_immediate_operand_comparable_object_iface_init(GComparableObjectInterface *); -}; +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_known_immediate_operand_hashable_object_iface_init(GHashableObjectInterface *); -/* Définition d'un remplacement d'opérande de valeur numérique (classe) */ -struct _GKnownImmOperandClass -{ - GImmOperandClass parent; /* Classe parente */ +/* Procède à l'initialisation de l'interface de sérialisation. */ +static void g_known_immediate_operand_serializable_object_iface_init(GSerializableObjectInterface *); -}; - - -/* Initialise la classe des remplacements d'opérandes. */ -static void g_known_imm_operand_class_init(GKnownImmOperandClass *); +/* Procède à l'initialisation de l'interface d'exportation. */ +static void g_known_immediate_operand_string_builder_iface_init(GStringBuilderInterface *); /* Initialise un remplacement d'opérande de valeur immédiate. */ -static void g_known_imm_operand_init(GKnownImmOperand *); - -/* Procède à l'initialisation de l'interface de renommage. */ -static void g_known_imm_operand_renamed_interface_init(GRenamedOperandInterface *); +static void g_known_immediate_operand_init(GKnownImmediateOperand *); /* Supprime toutes les références externes. */ -static void g_known_imm_operand_dispose(GKnownImmOperand *); +static void g_known_immediate_operand_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_known_imm_operand_finalize(GKnownImmOperand *); +static void g_known_immediate_operand_finalize(GObject *); + + +/* ---------------------- COMPARAISON DETAILLEE DE DEUX OBJETS ---------------------- */ -/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ +/* Réalise une comparaison étendue entre objets. */ +static int g_known_immediate_operand_compare(const GComparableObject *, const GComparableObject *); -/* Compare un opérande avec un autre. */ -static int g_known_imm_operand_compare(const GKnownImmOperand *, const GKnownImmOperand *, bool); -/* Traduit un opérande en version humainement lisible. */ -static void g_known_imm_operand_print(const GKnownImmOperand *, GBufferLine *); +/* ---------------------- CALCUL D'UNE EMPREINTE DE L'INSTANCE ---------------------- */ -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint g_known_imm_operand_hash(const GKnownImmOperand *, bool); -/* Charge un contenu depuis une mémoire tampon. */ -static bool g_known_imm_operand_load(GKnownImmOperand *, GObjectStorage *, packed_buffer_t *); +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint g_known_immediate_operand_hash(const GHashableObject *); -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool g_known_imm_operand_store(GKnownImmOperand *, GObjectStorage *, packed_buffer_t *); +/* ------------------- MECANISMES DE CONSERVATION ET RESTAURATION ------------------- */ -/* ------------------------- AFFICHAGE D'UN CONTENU RENOMME ------------------------- */ +/* Charge un objet depuis un flux de données. */ +static bool g_known_immediate_operand_load(GSerializableObject *, GObjectStorage *, int); -/* Fournit un texte comme représentation alternative d'opérande. */ -static const char *g_known_imm_operand_get_text(const GKnownImmOperand *); +/* Sauvegarde un objet dans un flux de données. */ +static bool g_known_immediate_operand_store(const GSerializableObject *, GObjectStorage *, int); + + + +/* ----------------- EXPORTATION SOUS FORME DE CHAINE DE CARACTERES ----------------- */ + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static bool g_known_immediate_operand_to_string(const GStringBuilder *, unsigned int, sized_binary_t *); @@ -108,8 +107,12 @@ static const char *g_known_imm_operand_get_text(const GKnownImmOperand *); /* Indique le type défini pour un remplacemet d'opérande de valeur numérique. */ -G_DEFINE_TYPE_WITH_CODE(GKnownImmOperand, g_known_imm_operand, G_TYPE_IMM_OPERAND, - G_IMPLEMENT_INTERFACE(G_TYPE_RENAMED_OPERAND, g_known_imm_operand_renamed_interface_init)); +G_DEFINE_TYPE_WITH_CODE(GKnownImmediateOperand, g_known_immediate_operand, G_TYPE_IMMEDIATE_OPERAND, + G_IMPLEMENT_INTERFACE(G_TYPE_COMPARABLE_OBJECT, g_known_immediate_operand_comparable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_HASHABLE_OBJECT, g_known_immediate_operand_hashable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_known_immediate_operand_serializable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_STRING_BUILDER, g_known_immediate_operand_string_builder_iface_init) + G_IMPLEMENT_INTERFACE_IF_SYM(g_arch_operand_ui_get_type, g_known_immediate_operand_ui_arch_operand_ui_iface_init)); /****************************************************************************** @@ -124,33 +127,42 @@ G_DEFINE_TYPE_WITH_CODE(GKnownImmOperand, g_known_imm_operand, G_TYPE_IMM_OPERAN * * ******************************************************************************/ -static void g_known_imm_operand_class_init(GKnownImmOperandClass *klass) +static void g_known_immediate_operand_class_init(GKnownImmediateOperandClass *klass) { GObjectClass *object; /* Autre version de la classe */ - GArchOperandClass *operand; /* Version de classe parente */ object = G_OBJECT_CLASS(klass); - operand = G_ARCH_OPERAND_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_known_imm_operand_dispose; - object->finalize = (GObjectFinalizeFunc)g_known_imm_operand_finalize; + object->dispose = g_known_immediate_operand_dispose; + object->finalize = g_known_immediate_operand_finalize; + +} - operand->compare = (operand_compare_fc)g_known_imm_operand_compare; - operand->print = (operand_print_fc)g_known_imm_operand_print; - operand->hash = (operand_hash_fc)g_known_imm_operand_hash; +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de comparaison. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - operand->load = (load_operand_fc)g_known_imm_operand_load; - operand->store = (store_operand_fc)g_known_imm_operand_store; +static void g_known_immediate_operand_comparable_object_iface_init(GComparableObjectInterface *iface) +{ + iface->compare = g_known_immediate_operand_compare; } /****************************************************************************** * * -* Paramètres : operand = instance à initialiser. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Initialise un remplacement d'opérande de valeur immédiate. * +* Description : Procède à l'initialisation de l'interface de détermination. * * * * Retour : - * * * @@ -158,9 +170,9 @@ static void g_known_imm_operand_class_init(GKnownImmOperandClass *klass) * * ******************************************************************************/ -static void g_known_imm_operand_init(GKnownImmOperand *operand) +static void g_known_immediate_operand_hashable_object_iface_init(GHashableObjectInterface *iface) { - operand->alt_text = NULL; + iface->hash = g_known_immediate_operand_hash; } @@ -169,7 +181,7 @@ static void g_known_imm_operand_init(GKnownImmOperand *operand) * * * Paramètres : iface = interface GLib à initialiser. * * * -* Description : Procède à l'initialisation de l'interface de renommage. * +* Description : Procède à l'initialisation de l'interface de sérialisation. * * * * Retour : - * * * @@ -177,18 +189,19 @@ static void g_known_imm_operand_init(GKnownImmOperand *operand) * * ******************************************************************************/ -static void g_known_imm_operand_renamed_interface_init(GRenamedOperandInterface *iface) +static void g_known_immediate_operand_serializable_object_iface_init(GSerializableObjectInterface *iface) { - iface->get_text = (get_renamed_text_fc)g_known_imm_operand_get_text; + iface->load = g_known_immediate_operand_load; + iface->store = g_known_immediate_operand_store; } /****************************************************************************** * * -* Paramètres : operand = instance d'objet GLib à traiter. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Supprime toutes les références externes. * +* Description : Procède à l'initialisation de l'interface d'exportation. * * * * Retour : - * * * @@ -196,12 +209,28 @@ static void g_known_imm_operand_renamed_interface_init(GRenamedOperandInterface * * ******************************************************************************/ -static void g_known_imm_operand_dispose(GKnownImmOperand *operand) +static void g_known_immediate_operand_string_builder_iface_init(GStringBuilderInterface *iface) { - if (operand->alt_text != NULL) - free(operand->alt_text); + iface->to_string = g_known_immediate_operand_to_string; - G_OBJECT_CLASS(g_known_imm_operand_parent_class)->dispose(G_OBJECT(operand)); +} + + +/****************************************************************************** +* * +* Paramètres : operand = instance à initialiser. * +* * +* Description : Initialise un remplacement d'opérande de valeur immédiate. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_known_immediate_operand_init(GKnownImmediateOperand *operand) +{ + operand->alt_text = NULL; } @@ -210,7 +239,7 @@ static void g_known_imm_operand_dispose(GKnownImmOperand *operand) * * * Paramètres : operand = instance d'objet GLib à traiter. * * * -* Description : Procède à la libération totale de la mémoire. * +* Description : Supprime toutes les références externes. * * * * Retour : - * * * @@ -218,180 +247,199 @@ static void g_known_imm_operand_dispose(GKnownImmOperand *operand) * * ******************************************************************************/ -static void g_known_imm_operand_finalize(GKnownImmOperand *operand) +static void g_known_immediate_operand_dispose(GObject *object) { - G_OBJECT_CLASS(g_known_imm_operand_parent_class)->finalize(G_OBJECT(operand)); + G_OBJECT_CLASS(g_known_immediate_operand_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : old = opérande à venir copier avant son remplacement. * -* alt = texte alternatif à présenter pour l'impression. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Crée un opérande remplaçant visuellement une valeur. * +* Description : Procède à la libération totale de la mémoire. * * * -* Retour : Instruction mise en place. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GArchOperand *g_known_imm_operand_new(const GImmOperand *old, const char *alt) +static void g_known_immediate_operand_finalize(GObject *object) { - GKnownImmOperand *result; /* Remplacement à retourner */ - immop_extra_data_t *src; /* Données insérées à consulter*/ - immop_extra_data_t *dest; /* Données insérées à modifier */ + GKnownImmediateOperand *operand; /* Version spécialisée */ - result = g_object_new(G_TYPE_KNOWN_IMM_OPERAND, NULL); + operand = G_KNOWN_IMMEDIATE_OPERAND(object); - result->parent.raw = old->raw; + if (operand->alt_text != NULL) + free(operand->alt_text); - src = GET_IMM_OP_EXTRA(old); - dest = GET_IMM_OP_EXTRA(&result->parent); + G_OBJECT_CLASS(g_known_immediate_operand_parent_class)->finalize(object); - LOCK_GOBJECT_EXTRA(src); +} - *(&dest->parent) = *(&src->parent); - dest->size = src->size; +/****************************************************************************** +* * +* Paramètres : old = opérande à venir copier avant son remplacement. * +* alt = texte alternatif à présenter pour l'impression. * +* * +* Description : Crée un opérande remplaçant visuellement une valeur. * +* * +* Retour : Instruction mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ - dest->def_display = src->def_display; - dest->display = src->display; +GArchOperand *g_known_immediate_operand_new(const GImmediateOperand *old, const char *alt) +{ + GKnownImmediateOperand *result; /* Remplacement à retourner */ - UNLOCK_GOBJECT_EXTRA(src); + result = g_object_new(G_TYPE_KNOWN_IMMEDIATE_OPERAND, NULL); - result->alt_text = strdup(alt); + if (!g_known_immediate_operand_create(result, old, alt)) + g_clear_object(&result); return G_ARCH_OPERAND(result); } - -/* ---------------------------------------------------------------------------------- */ -/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ -/* ---------------------------------------------------------------------------------- */ - - /****************************************************************************** * * -* Paramètres : a = premier opérande à consulter. * -* b = second opérande à consulter. * -* lock = précise le besoin en verrouillage. * +* Paramètres : operand = instance à initialiser pleinement. * +* old = opérande à venir copier avant son remplacement. * +* alt = texte alternatif à présenter pour l'impression. * * * -* Description : Compare un opérande avec un autre. * +* Description : Met en place un opérande remplaçant visuellement une valeur. * * * -* Retour : Bilan de la comparaison. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static int g_known_imm_operand_compare(const GKnownImmOperand *a, const GKnownImmOperand *b, bool lock) +bool g_known_immediate_operand_create(GKnownImmediateOperand *operand, const GImmediateOperand *old, const char *alt) { - int result; /* Bilan à retourner */ - immop_extra_data_t *ea; /* Données insérées à consulter*/ - immop_extra_data_t *eb; /* Données insérées à consulter*/ - GArchOperandClass *class; /* Classe parente normalisée */ + bool result; /* Bilan à retourner */ + immop_extra_data_t extra; /* Données insérées à consulter*/ - ea = GET_IMM_OP_EXTRA(G_IMM_OPERAND(a)); - eb = GET_IMM_OP_EXTRA(G_IMM_OPERAND(b)); + result = true; - if (lock) - { - LOCK_GOBJECT_EXTRA(ea); - LOCK_GOBJECT_EXTRA(eb); - } + extra = GET_IMM_OP_EXTRA(old); - result = strcmp(a->alt_text, b->alt_text); + SET_IMM_OP_EXTRA(operand, &extra); - if (result == 0) - { - class = G_ARCH_OPERAND_CLASS(g_known_imm_operand_parent_class); - result = class->compare(G_ARCH_OPERAND(a), G_ARCH_OPERAND(b), false); - } + G_IMMEDIATE_OPERAND(operand)->raw = G_IMMEDIATE_OPERAND(old)->raw; - if (lock) - { - UNLOCK_GOBJECT_EXTRA(eb); - UNLOCK_GOBJECT_EXTRA(ea); - } + operand->alt_text = strdup(alt); return result; } + +/* ---------------------------------------------------------------------------------- */ +/* COMPARAISON DETAILLEE DE DEUX OBJETS */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = opérande à traiter. * -* line = ligne tampon où imprimer l'opérande donné. * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * * * -* Description : Traduit un opérande en version humainement lisible. * +* Description : Réalise une comparaison étendue entre objets. * * * -* Retour : - * +* Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ -static void g_known_imm_operand_print(const GKnownImmOperand *operand, GBufferLine *line) +static int g_known_immediate_operand_compare(const GComparableObject *object, const GComparableObject *other) { - size_t len; /* Taille de l'élément inséré */ + int result; /* Bilan à retourner */ + GComparableObjectInterface *iface; /* Interface utilisée */ + GComparableObjectInterface *parent_iface; /* Interface parente */ + GKnownImmediateOperand *operand_a; /* Version spécialisée #0 */ + GKnownImmediateOperand *operand_b; /* Version spécialisée #1 */ + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); - len = strlen(operand->alt_text); + parent_iface = g_type_interface_peek_parent(iface); - g_buffer_line_append_text(line, DLC_ASSEMBLY, operand->alt_text, len, RTT_IMMEDIATE, G_OBJECT(operand)); + result = parent_iface->compare(object, other); + + if (result == 0) + { + operand_a = G_KNOWN_IMMEDIATE_OPERAND(object); + operand_b = G_KNOWN_IMMEDIATE_OPERAND(other); + + result = strcmp(operand_a->alt_text, operand_b->alt_text); + + } + + return result; } +/* ---------------------------------------------------------------------------------- */ +/* CALCUL D'UNE EMPREINTE DE L'INSTANCE */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = objet dont l'instance se veut unique. * -* lock = précise le besoin en verrouillage. * +* Paramètres : object = objet dont l'instance est à consulter. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static guint g_known_imm_operand_hash(const GKnownImmOperand *operand, bool lock) +static guint g_known_immediate_operand_hash(const GHashableObject *object) { guint result; /* Valeur à retourner */ - immop_extra_data_t *extra; /* Données insérées à consulter*/ - GArchOperandClass *class; /* Classe parente normalisée */ + GHashableObjectInterface *iface; /* Interface utilisée */ + GHashableObjectInterface *parent_iface; /* Interface parente */ + GKnownImmediateOperand *operand; /* Version spécialisée */ - extra = GET_IMM_OP_EXTRA(G_IMM_OPERAND(operand)); + iface = G_HASHABLE_OBJECT_GET_IFACE(object); - if (lock) - LOCK_GOBJECT_EXTRA(extra); + parent_iface = g_type_interface_peek_parent(iface); - class = G_ARCH_OPERAND_CLASS(g_known_imm_operand_parent_class); - result = class->hash(G_ARCH_OPERAND(operand), false); + result = parent_iface->hash(object); - result ^= g_str_hash(operand->alt_text); + operand = G_KNOWN_IMMEDIATE_OPERAND(object); - if (lock) - UNLOCK_GOBJECT_EXTRA(extra); + result ^= g_str_hash(operand->alt_text); return result; } + +/* ---------------------------------------------------------------------------------- */ +/* MECANISMES DE CONSERVATION ET RESTAURATION */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * +* Paramètres : object = élément GLib à constuire. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * * * -* Description : Charge un contenu depuis une mémoire tampon. * +* Description : Charge un objet depuis un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -399,30 +447,31 @@ static guint g_known_imm_operand_hash(const GKnownImmOperand *operand, bool lock * * ******************************************************************************/ -static bool g_known_imm_operand_load(GKnownImmOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_known_immediate_operand_load(GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ - GArchOperandClass *parent; /* Classe parente à consulter */ - rle_string str; /* Chaîne à charger */ + GSerializableObjectInterface *iface; /* Interface utilisée */ + GSerializableObjectInterface *parent_iface; /* Interface parente */ + sized_binary_t str; /* Texte alternatif rechargé */ + GKnownImmediateOperand *operand; /* Version spécialisée */ + + iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); - parent = G_ARCH_OPERAND_CLASS(g_known_imm_operand_parent_class); + parent_iface = g_type_interface_peek_parent(iface); - result = parent->load(G_ARCH_OPERAND(operand), storage, pbuf); + result = parent_iface->load(object, storage, fd); if (result) { - setup_empty_rle_string(&str); + init_sized_binary(&str); - result = unpack_rle_string(&str, pbuf); + load_sized_binary_as_string(&str, fd); - if (result) - { - if (get_rle_string(&str) != NULL) - operand->alt_text = strdup(get_rle_string(&str)); + operand = G_KNOWN_IMMEDIATE_OPERAND(object); - exit_rle_string(&str); + operand->alt_text = strdup(str.static_data); - } + exit_sized_binary(&str); } @@ -433,11 +482,11 @@ static bool g_known_imm_operand_load(GKnownImmOperand *operand, GObjectStorage * /****************************************************************************** * * -* Paramètres : operand = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* Paramètres : object = élément GLib à consulter. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * * * -* Description : Sauvegarde un contenu dans une mémoire tampon. * +* Description : Sauvegarde un objet dans un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -445,54 +494,64 @@ static bool g_known_imm_operand_load(GKnownImmOperand *operand, GObjectStorage * * * ******************************************************************************/ -static bool g_known_imm_operand_store(GKnownImmOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_known_immediate_operand_store(const GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ - GArchOperandClass *parent; /* Classe parente à consulter */ - rle_string str; /* Chaîne à conserver */ + GSerializableObjectInterface *iface; /* Interface utilisée */ + GSerializableObjectInterface *parent_iface; /* Interface parente */ + GKnownImmediateOperand *operand; /* Version spécialisée */ + sized_binary_t str; /* Texte alternatif à conserver*/ - parent = G_ARCH_OPERAND_CLASS(g_known_imm_operand_parent_class); + iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); - result = parent->store(G_ARCH_OPERAND(operand), storage, pbuf); + parent_iface = g_type_interface_peek_parent(iface); - if (result) - { - init_static_rle_string(&str, operand->alt_text); + result = parent_iface->store(object, storage, fd); + if (!result) goto exit; - result = pack_rle_string(&str, pbuf); + operand = G_KNOWN_IMMEDIATE_OPERAND(object); - exit_rle_string(&str); + setup_sized_binary_from_static_string(&str, operand->alt_text); - } + result = store_sized_binary_as_string(&str, fd); + + exit: return result; } - /* ---------------------------------------------------------------------------------- */ -/* AFFICHAGE D'UN CONTENU RENOMME */ +/* EXPORTATION SOUS FORME DE CHAINE DE CARACTERES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : operand = operande à consulter. * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * * * -* Description : Fournit un texte comme représentation alternative d'opérande.* +* Description : Exporte une chaîne de caractères à partir d'un objet. * * * -* Retour : Chaîne de caractère de représentation alternative. * +* Retour : Bilan de l'opération. * * * -* Remarques : - * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * * * ******************************************************************************/ -static const char *g_known_imm_operand_get_text(const GKnownImmOperand *operand) +static bool g_known_immediate_operand_to_string(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) { - const char *result; /* Texte à retourner */ + bool result; /* Bilan à retourner */ + const GKnownImmediateOperand *operand; /* Version spécialisée */ + + result = true; + + operand = G_KNOWN_IMMEDIATE_OPERAND(builder); - result = operand->alt_text; + add_to_sized_binary(out, operand->alt_text, strlen(operand->alt_text)); return result; diff --git a/src/arch/operands/known.h b/src/arch/operands/known.h index eb84d3b..a8b563f 100644 --- a/src/arch/operands/known.h +++ b/src/arch/operands/known.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * known.h - prototypes pour les opérandes représentant des valeurs numériques avec sémantique * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,34 +25,18 @@ #define _ARCH_OPERANDS_KNOWN_H -#include <glib-object.h> - - #include "immediate.h" -#include "../operand.h" - - - -#define G_TYPE_KNOWN_IMM_OPERAND g_known_imm_operand_get_type() -#define G_KNOWN_IMM_OPERAND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KNOWN_IMM_OPERAND, GKnownImmOperand)) -#define G_IS_KNOWN_IMM_OPERAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KNOWN_IMM_OPERAND)) -#define G_KNOWN_IMM_OPERAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KNOWN_IMM_OPERAND, GKnownImmOperandClass)) -#define G_IS_KNOWN_IMM_OPERAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KNOWN_IMM_OPERAND)) -#define G_KNOWN_IMM_OPERAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KNOWN_IMM_OPERAND, GKnownImmOperandClass)) +#include "../../glibext/helpers.h" -/* Définition d'un remplacement d'opérande de valeur numérique (instance) */ -typedef struct _GKnownImmOperand GKnownImmOperand; -/* Définition d'un remplacement d'opérande de valeur numérique (classe) */ -typedef struct _GKnownImmOperandClass GKnownImmOperandClass; +#define G_TYPE_KNOWN_IMMEDIATE_OPERAND (g_known_immediate_operand_get_type()) +DECLARE_GTYPE(GKnownImmediateOperand, g_known_immediate_operand, G, KNOWN_IMMEDIATE_OPERAND); -/* Indique le type défini pour un remplacemet d'opérande de valeur numérique. */ -GType g_known_imm_operand_get_type(void); /* Crée un opérande remplaçant visuellement une valeur. */ -GArchOperand *g_known_imm_operand_new(const GImmOperand *, const char *); +GArchOperand *g_known_immediate_operand_new(const GImmediateOperand *, const char *); diff --git a/src/arch/operands/register-int.h b/src/arch/operands/register-int.h index a887567..93cf025 100644 --- a/src/arch/operands/register-int.h +++ b/src/arch/operands/register-int.h @@ -26,8 +26,6 @@ #include "register.h" - - #include "../operand-int.h" @@ -49,5 +47,9 @@ struct _GRegisterOperandClass }; +/* Met en place un opérande réprésentant une valeur numérique. */ +bool g_register_operand_create(GRegisterOperand *, GArchRegister *); + + #endif /* _ARCH_OPERANDS_REGISTER_INT_H */ diff --git a/src/arch/operands/register-ui.c b/src/arch/operands/register-ui.c new file mode 100644 index 0000000..513a24f --- /dev/null +++ b/src/arch/operands/register-ui.c @@ -0,0 +1,93 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * register-ui.c - opérandes représentant des registres sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "register-ui.h" + + +#include "register.h" +#include "../../glibext/strbuilder.h" +#include "../../glibext/options/asm.h" + + + +/* Traduit un opérande en version humainement lisible. */ +static void g_register_operand_ui_print(const GArchOperandUI *, GBufferLine *); + + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'opérande UI. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_register_operand_ui_arch_operand_ui_iface_init(GArchOperandUIInterface *iface) +{ + iface->print = g_register_operand_ui_print; + iface->build_tooltip = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : operand = opérande à traiter. * +* line = ligne tampon où imprimer l'opérande donné. * +* * +* Description : Traduit un opérande en version humainement lisible. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_register_operand_ui_print(const GArchOperandUI *operand, GBufferLine *line) +{ + GStringBuilder *builder; /* Autre version de l'opérande */ + sized_binary_t str; /* Chaîne équivalente produite */ + bool status; /* Bilan d'une conversion */ + + builder = G_STRING_BUILDER(operand); + + init_sized_binary(&str); + + status = g_string_builder_to_string(builder, 0 /* flags */, &str); + + if (status) + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_REGISTER, + str.static_data, str.size, NULL, G_OBJECT(operand)); + + else + g_buffer_line_append_text(line, ACO_ASSEMBLY, TRT_REGISTER, + "??", 2, NULL, G_OBJECT(operand)); + + exit_sized_binary(&str); + +} diff --git a/src/arch/operands/register-ui.h b/src/arch/operands/register-ui.h new file mode 100644 index 0000000..16be67b --- /dev/null +++ b/src/arch/operands/register-ui.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * register-ui.h - prototypes pour les opérandes représentant des registres sous forme graphique + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ARCH_OPERANDS_REGISTER_UI_H +#define _ARCH_OPERANDS_REGISTER_UI_H + + +#include "../operand-ui-int.h" // FIXME ?? + + + +/* Procède à l'initialisation de l'interface d'opérande UI. */ +void g_register_operand_ui_arch_operand_ui_iface_init(GArchOperandUIInterface *); + + + +#endif /* _ARCH_OPERANDS_REGISTER_UI_H */ diff --git a/src/arch/operands/register.c b/src/arch/operands/register.c index 4615a99..cad2c4e 100644 --- a/src/arch/operands/register.c +++ b/src/arch/operands/register.c @@ -28,7 +28,10 @@ #include "register-int.h" -#include "../storage.h" +#include "../../glibext/comparable-int.h" +#include "../../glibext/hashable-int.h" +#include "../../glibext/serialize-int.h" +#include "../../glibext/strbuilder-int.h" @@ -38,34 +41,61 @@ /* Initialise la classe des opérandes de registre. */ static void g_register_operand_class_init(GRegisterOperandClass *); +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_register_operand_comparable_object_iface_init(GComparableObjectInterface *); + +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_register_operand_hashable_object_iface_init(GHashableObjectInterface *); + +/* Procède à l'initialisation de l'interface de sérialisation. */ +static void g_register_operand_serializable_object_iface_init(GSerializableObjectInterface *); + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void g_register_operand_string_builder_iface_init(GStringBuilderInterface *); + /* Initialise une instance d'opérande de registre. */ static void g_register_operand_init(GRegisterOperand *); /* Supprime toutes les références externes. */ -static void g_register_operand_dispose(GRegisterOperand *); +static void g_register_operand_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_register_operand_finalize(GRegisterOperand *); +static void g_register_operand_finalize(GObject *); + + + +/* ---------------------- COMPARAISON DETAILLEE DE DEUX OBJETS ---------------------- */ + + +/* Réalise une comparaison étendue entre objets. */ +static int g_register_operand_compare(const GComparableObject *, const GComparableObject *); + +/* ---------------------- CALCUL D'UNE EMPREINTE DE L'INSTANCE ---------------------- */ -/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint g_register_operand_hash(const GHashableObject *); -/* Compare un opérande avec un autre. */ -static int g_register_operand_compare(const GRegisterOperand *, const GRegisterOperand *, bool); -/* Traduit un opérande en version humainement lisible. */ -static void g_register_operand_print(const GRegisterOperand *, GBufferLine *); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint g_register_operand_hash(const GRegisterOperand *, bool); +/* ------------------- MECANISMES DE CONSERVATION ET RESTAURATION ------------------- */ -/* Charge un contenu depuis une mémoire tampon. */ -static bool g_register_operand_load(GRegisterOperand *, GObjectStorage *, packed_buffer_t *); -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool g_register_operand_store(GRegisterOperand *, GObjectStorage *, packed_buffer_t *); +/* Charge un objet depuis un flux de données. */ +static bool g_register_operand_load(GSerializableObject *, GObjectStorage *, int); + +/* Sauvegarde un objet dans un flux de données. */ +static bool g_register_operand_store(const GSerializableObject *, GObjectStorage *, int); + + + +/* ----------------- EXPORTATION SOUS FORME DE CHAINE DE CARACTERES ----------------- */ + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static bool g_register_operand_to_string(const GStringBuilder *, unsigned int, sized_binary_t *); @@ -75,7 +105,12 @@ static bool g_register_operand_store(GRegisterOperand *, GObjectStorage *, packe /* Indique le type défini par la GLib pour un opérande de registre Dalvik. */ -G_DEFINE_TYPE(GRegisterOperand, g_register_operand, G_TYPE_ARCH_OPERAND); +G_DEFINE_TYPE_WITH_CODE(GRegisterOperand, g_register_operand, G_TYPE_ARCH_OPERAND, + G_IMPLEMENT_INTERFACE(G_TYPE_COMPARABLE_OBJECT, g_register_operand_comparable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_HASHABLE_OBJECT, g_register_operand_hashable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_register_operand_serializable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_STRING_BUILDER, g_register_operand_string_builder_iface_init) + G_IMPLEMENT_INTERFACE_IF_SYM(g_arch_operand_ui_get_type, g_register_operand_ui_arch_operand_ui_iface_init)); /****************************************************************************** @@ -93,23 +128,88 @@ G_DEFINE_TYPE(GRegisterOperand, g_register_operand, G_TYPE_ARCH_OPERAND); static void g_register_operand_class_init(GRegisterOperandClass *klass) { GObjectClass *object; /* Autre version de la classe */ - GArchOperandClass *operand; /* Version de classe parente */ object = G_OBJECT_CLASS(klass); - operand = G_ARCH_OPERAND_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_register_operand_dispose; - object->finalize = (GObjectFinalizeFunc)g_register_operand_finalize; + object->dispose = g_register_operand_dispose; + object->finalize = g_register_operand_finalize; - operand = G_ARCH_OPERAND_CLASS(klass); +} - operand->compare = (operand_compare_fc)g_register_operand_compare; - operand->print = (operand_print_fc)g_register_operand_print; - operand->hash = (operand_hash_fc)g_register_operand_hash; +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de comparaison. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - operand->load = (load_operand_fc)g_register_operand_load; - operand->store = (store_operand_fc)g_register_operand_store; +static void g_register_operand_comparable_object_iface_init(GComparableObjectInterface *iface) +{ + iface->compare = g_register_operand_compare; + +} + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_register_operand_hashable_object_iface_init(GHashableObjectInterface *iface) +{ + iface->hash = g_register_operand_hash; + +} + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de sérialisation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_register_operand_serializable_object_iface_init(GSerializableObjectInterface *iface) +{ + iface->load = g_register_operand_load; + iface->store = g_register_operand_store; + +} + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_register_operand_string_builder_iface_init(GStringBuilderInterface *iface) +{ + iface->to_string = g_register_operand_to_string; } @@ -135,7 +235,7 @@ static void g_register_operand_init(GRegisterOperand *operand) /****************************************************************************** * * -* Paramètres : binary = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -145,18 +245,22 @@ static void g_register_operand_init(GRegisterOperand *operand) * * ******************************************************************************/ -static void g_register_operand_dispose(GRegisterOperand *operand) +static void g_register_operand_dispose(GObject *object) { + GRegisterOperand *operand; /* Version spécialisée */ + + operand = G_REGISTER_OPERAND(object); + g_clear_object(&operand->reg); - G_OBJECT_CLASS(g_register_operand_parent_class)->dispose(G_OBJECT(operand)); + G_OBJECT_CLASS(g_register_operand_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : binary = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -166,9 +270,36 @@ static void g_register_operand_dispose(GRegisterOperand *operand) * * ******************************************************************************/ -static void g_register_operand_finalize(GRegisterOperand *operand) +static void g_register_operand_finalize(GObject *object) +{ + G_OBJECT_CLASS(g_register_operand_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : operand = instance à initialiser pleinement. * +* reg = registre matériel à représenter. * +* * +* Description : Met en place un opérande réprésentant une valeur numérique. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_register_operand_create(GRegisterOperand *operand, GArchRegister *reg) { - G_OBJECT_CLASS(g_register_operand_parent_class)->finalize(G_OBJECT(operand)); + bool result; /* Bilan à retourner */ + + result = true; + + operand->reg = reg; + ref_object(reg); + + return result; } @@ -190,8 +321,7 @@ GArchRegister *g_register_operand_get_register(const GRegisterOperand *operand) GArchRegister *result; /* Instance à retourner */ result = operand->reg; - - g_object_ref(G_OBJECT(result)); + ref_object(result); return result; @@ -200,17 +330,16 @@ GArchRegister *g_register_operand_get_register(const GRegisterOperand *operand) /* ---------------------------------------------------------------------------------- */ -/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* COMPARAISON DETAILLEE DE DEUX OBJETS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : a = premier opérande à consulter. * -* b = second opérande à consulter. * -* lock = précise le besoin en verrouillage. * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * * * -* Description : Compare un opérande avec un autre. * +* Description : Réalise une comparaison étendue entre objets. * * * * Retour : Bilan de la comparaison. * * * @@ -218,17 +347,25 @@ GArchRegister *g_register_operand_get_register(const GRegisterOperand *operand) * * ******************************************************************************/ -static int g_register_operand_compare(const GRegisterOperand *a, const GRegisterOperand *b, bool lock) +static int g_register_operand_compare(const GComparableObject *object, const GComparableObject *other) { int result; /* Bilan à retourner */ - GArchOperandClass *class; /* Classe parente normalisée */ + GComparableObjectInterface *iface; /* Interface utilisée */ + GComparableObjectInterface *parent_iface; /* Interface parente */ + GRegisterOperand *operand; /* Version spécialisée */ + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); - result = g_arch_register_compare(a->reg, b->reg); + parent_iface = g_type_interface_peek_parent(iface); + + result = parent_iface->compare(object, other); if (result == 0) { - class = G_ARCH_OPERAND_CLASS(g_register_operand_parent_class); - result = class->compare(G_ARCH_OPERAND(a), G_ARCH_OPERAND(b), false); + operand = G_REGISTER_OPERAND(object); + + result = g_comparable_object_compare(G_COMPARABLE_OBJECT(operand->reg), other); + } return result; @@ -236,53 +373,96 @@ static int g_register_operand_compare(const GRegisterOperand *a, const GRegister } + +/* ---------------------------------------------------------------------------------- */ +/* CALCUL D'UNE EMPREINTE DE L'INSTANCE */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = opérande à traiter. * -* line = ligne tampon où imprimer l'opérande donné. * +* Paramètres : object = objet dont l'instance est à consulter. * * * -* Description : Traduit un opérande en version humainement lisible. * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * * * -* Retour : - * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static void g_register_operand_print(const GRegisterOperand *operand, GBufferLine *line) +static guint g_register_operand_hash(const GHashableObject *object) { - g_arch_register_print(operand->reg, line); + guint result; /* Valeur à retourner */ + GHashableObjectInterface *iface; /* Interface utilisée */ + GHashableObjectInterface *parent_iface; /* Interface parente */ + GRegisterOperand *operand; /* Version spécialisée */ + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + result = parent_iface->hash(object); + + operand = G_REGISTER_OPERAND(object); + + result ^= g_hashable_object_hash(G_HASHABLE_OBJECT(operand->reg)); + + return result; } + +/* ---------------------------------------------------------------------------------- */ +/* MECANISMES DE CONSERVATION ET RESTAURATION */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = objet dont l'instance se veut unique. * -* lock = précise le besoin en verrouillage. * +* Paramètres : object = élément GLib à constuire. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Charge un objet depuis un flux de données. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static guint g_register_operand_hash(const GRegisterOperand *operand, bool lock) +static bool g_register_operand_load(GSerializableObject *object, GObjectStorage *storage, int fd) { - guint result; /* Valeur à retourner */ - GArchOperandClass *class; /* Classe parente normalisée */ - GArchRegister *reg; /* Registre visé par l'opérande*/ + bool result; /* Bilan à retourner */ + GSerializableObjectInterface *iface; /* Interface utilisée */ + GSerializableObjectInterface *parent_iface; /* Interface parente */ + GSerializableObject *reg; /* Registre récupéré */ + GRegisterOperand *operand; /* Version spécialisée */ + + iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + result = parent_iface->load(object, storage, fd); + + if (result) + { + reg = g_object_storage_unpack_object(storage, fd, "registers"); - class = G_ARCH_OPERAND_CLASS(g_register_operand_parent_class); - result = class->hash(G_ARCH_OPERAND(operand), false); + if (reg == NULL) + result = false; - reg = g_register_operand_get_register(operand); + else + { + operand = G_REGISTER_OPERAND(object); - result ^= g_arch_register_hash(reg); + operand->reg = G_ARCH_REGISTER(reg); + + } - g_object_unref(G_OBJECT(reg)); + } return result; @@ -291,11 +471,11 @@ static guint g_register_operand_hash(const GRegisterOperand *operand, bool lock) /****************************************************************************** * * -* Paramètres : operand = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * +* Paramètres : object = élément GLib à consulter. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * * * -* Description : Charge un contenu depuis une mémoire tampon. * +* Description : Sauvegarde un objet dans un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -303,61 +483,64 @@ static guint g_register_operand_hash(const GRegisterOperand *operand, bool lock) * * ******************************************************************************/ -static bool g_register_operand_load(GRegisterOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_register_operand_store(const GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ - GArchOperandClass *parent; /* Classe parente à consulter */ - GSerializableObject *reg; /* Registre manipulé */ + GRegisterOperand *operand; /* Version spécialisée */ + off64_t reg_pos; /* Position renvoyant au reg. */ + GSerializableObjectInterface *iface; /* Interface utilisée */ + GSerializableObjectInterface *parent_iface; /* Interface parente */ - parent = G_ARCH_OPERAND_CLASS(g_register_operand_parent_class); + operand = G_REGISTER_OPERAND(object); - result = parent->load(G_ARCH_OPERAND(operand), storage, pbuf); + result = g_object_storage_store_object(storage, "registers", G_SERIALIZABLE_OBJECT(operand->reg), ®_pos); + if (!result) goto exit; - if (result) - { - reg = g_object_storage_unpack_object(storage, "registers", pbuf); + iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); - result = (reg != NULL); + parent_iface = g_type_interface_peek_parent(iface); - if (result) - operand->reg = G_ARCH_REGISTER(reg); + result = parent_iface->store(object, storage, fd); + if (!result) goto exit; - } + result = store_uleb128((uleb128_t []) { reg_pos }, fd); + + exit: return result; } + +/* ---------------------------------------------------------------------------------- */ +/* EXPORTATION SOUS FORME DE CHAINE DE CARACTERES */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : operand = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * * * -* Description : Sauvegarde un contenu dans une mémoire tampon. * +* Description : Exporte une chaîne de caractères à partir d'un objet. * * * * Retour : Bilan de l'opération. * * * -* Remarques : - * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * * * ******************************************************************************/ -static bool g_register_operand_store(GRegisterOperand *operand, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_register_operand_to_string(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) { bool result; /* Bilan à retourner */ - GArchOperandClass *parent; /* Classe parente à consulter */ - GSerializableObject *reg; /* Registre manipulé */ + const GRegisterOperand *operand; /* Version spécialisée */ - parent = G_ARCH_OPERAND_CLASS(g_register_operand_parent_class); + operand = G_REGISTER_OPERAND(builder); - result = parent->store(G_ARCH_OPERAND(operand), storage, pbuf); - - if (result) - { - reg = G_SERIALIZABLE_OBJECT(operand->reg); - result = g_object_storage_pack_object(storage, "registers", reg, pbuf); - } + result = g_string_builder_to_string(G_STRING_BUILDER(operand->reg), flags, out); return result; diff --git a/src/arch/register-int.h b/src/arch/register-int.h index f0b9af9..22ef2cc 100644 --- a/src/arch/register-int.h +++ b/src/arch/register-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * register-int.h - définitions internes pour la représentation générique d'un registre * - * Copyright (C) 2012-2018 Cyrille Bagard + * Copyright (C) 2012-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,31 +26,15 @@ #include "register.h" -#include "../analysis/storage/serialize-int.h" -/* Produit une empreinte à partir d'un registre. */ -typedef guint (* reg_hash_fc) (const GArchRegister *); - -/* Compare un registre avec un autre. */ -typedef int (* reg_compare_fc) (const GArchRegister *, const GArchRegister *); - -/* Traduit un registre en version humainement lisible. */ -typedef void (* reg_print_fc) (const GArchRegister *, GBufferLine *); - /* Indique si le registre correspond à ebp ou similaire. */ typedef bool (* reg_is_base_pointer_fc) (const GArchRegister *); /* Indique si le registre correspond à esp ou similaire. */ typedef bool (* reg_is_stack_pointer_fc) (const GArchRegister *); -/* Charge un contenu depuis une mémoire tampon. */ -typedef bool (* load_register_fc) (GArchRegister *, GObjectStorage *, packed_buffer_t *); - -/* Sauvegarde un contenu dans une mémoire tampon. */ -typedef bool (* store_register_fc) (GArchRegister *, GObjectStorage *, packed_buffer_t *); - /* Représentation d'un registre (instance) */ struct _GArchRegister @@ -64,15 +48,9 @@ struct _GArchRegisterClass { GObjectClass parent; /* A laisser en premier */ - reg_hash_fc hash; /* Production d'empreinte */ - reg_compare_fc compare; /* Comparaison de registres */ - reg_print_fc print; /* Impression du registre */ reg_is_base_pointer_fc is_bp; /* Correspondance avec ebp */ reg_is_stack_pointer_fc is_sp; /* Correspondance avec esp */ - load_register_fc load; /* Chargement depuis un tampon */ - store_register_fc store; /* Conservation dans un tampon */ - }; diff --git a/src/arch/register.c b/src/arch/register.c index f487419..6017373 100644 --- a/src/arch/register.c +++ b/src/arch/register.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * registers.c - aides auxiliaires relatives aux registres Dalvik * - * Copyright (C) 2012-2018 Cyrille Bagard + * Copyright (C) 2012-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,6 +25,10 @@ #include "register-int.h" +#include "../glibext/comparable-int.h" +#include "../glibext/hashable-int.h" +#include "../glibext/serialize-int.h" +#include "../glibext/strbuilder-int.h" @@ -34,34 +38,26 @@ /* Initialise la classe des registres. */ static void g_arch_register_class_init(GArchRegisterClass *); -/* Initialise une instance de registre. */ -static void g_arch_register_init(GArchRegister *); +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_arch_register_comparable_object_iface_init(GComparableObjectInterface *); + +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_arch_register_hashable_object_iface_init(GHashableObjectInterface *); /* Procède à l'initialisation de l'interface de sérialisation. */ static void g_arch_register_serializable_init(GSerializableObjectInterface *); -/* Supprime toutes les références externes. */ -static void g_arch_register_dispose(GArchRegister *); - -/* Procède à la libération totale de la mémoire. */ -static void g_arch_register_finalize(GArchRegister *); - +/* Procède à l'initialisation de l'interface d'exportation. */ +static void g_arch_register_string_builder_iface_init(GStringBuilderInterface *); +/* Initialise une instance de registre. */ +static void g_arch_register_init(GArchRegister *); -/* -------------------- CONSERVATION ET RECHARGEMENT DES DONNEES -------------------- */ - - -/* Charge un contenu depuis une mémoire tampon. */ -static bool _g_arch_register_load(GArchRegister *, GObjectStorage *, packed_buffer_t *); - -/* Charge un contenu depuis une mémoire tampon. */ -static bool g_arch_register_load(GArchRegister *, GObjectStorage *, packed_buffer_t *); - -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool _g_arch_register_store(GArchRegister *, GObjectStorage *, packed_buffer_t *); +/* Supprime toutes les références externes. */ +static void g_arch_register_dispose(GObject *); -/* Sauvegarde un contenu dans une mémoire tampon. */ -static bool g_arch_register_store(GArchRegister *, GObjectStorage *, packed_buffer_t *); +/* Procède à la libération totale de la mémoire. */ +static void g_arch_register_finalize(GObject *); @@ -72,7 +68,10 @@ static bool g_arch_register_store(GArchRegister *, GObjectStorage *, packed_buff /* Indique le type défini pour une représentation d'un registre. */ G_DEFINE_TYPE_WITH_CODE(GArchRegister, g_arch_register, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_arch_register_serializable_init)); + G_IMPLEMENT_INTERFACE(G_TYPE_COMPARABLE_OBJECT, g_arch_register_comparable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_HASHABLE_OBJECT, g_arch_register_hashable_object_iface_init) + G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_arch_register_serializable_init) + G_IMPLEMENT_INTERFACE(G_TYPE_STRING_BUILDER, g_arch_register_string_builder_iface_init)); /****************************************************************************** @@ -93,20 +92,18 @@ static void g_arch_register_class_init(GArchRegisterClass *klass) object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_arch_register_dispose; - object->finalize = (GObjectFinalizeFunc)g_arch_register_finalize; - - klass->load = (load_register_fc)_g_arch_register_load; - klass->store = (store_register_fc)_g_arch_register_store; + object->dispose = g_arch_register_dispose; + object->finalize = g_arch_register_finalize; } + /****************************************************************************** * * -* Paramètres : reg = instance à initialiser. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Initialise une instance de registre. * +* Description : Procède à l'initialisation de l'interface de comparaison. * * * * Retour : - * * * @@ -114,8 +111,9 @@ static void g_arch_register_class_init(GArchRegisterClass *klass) * * ******************************************************************************/ -static void g_arch_register_init(GArchRegister *reg) +static void g_arch_register_comparable_object_iface_init(GComparableObjectInterface *iface) { + iface->compare = NULL; } @@ -124,7 +122,7 @@ static void g_arch_register_init(GArchRegister *reg) * * * Paramètres : iface = interface GLib à initialiser. * * * -* Description : Procède à l'initialisation de l'interface de sérialisation. * +* Description : Procède à l'initialisation de l'interface de détermination. * * * * Retour : - * * * @@ -132,19 +130,18 @@ static void g_arch_register_init(GArchRegister *reg) * * ******************************************************************************/ -static void g_arch_register_serializable_init(GSerializableObjectInterface *iface) +static void g_arch_register_hashable_object_iface_init(GHashableObjectInterface *iface) { - iface->load = (load_serializable_object_cb)g_arch_register_load; - iface->store = (store_serializable_object_cb)g_arch_register_store; + iface->hash = NULL; } /****************************************************************************** * * -* Paramètres : reg = instance d'objet GLib à traiter. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Supprime toutes les références externes. * +* Description : Procède à l'initialisation de l'interface de sérialisation. * * * * Retour : - * * * @@ -152,18 +149,19 @@ static void g_arch_register_serializable_init(GSerializableObjectInterface *ifac * * ******************************************************************************/ -static void g_arch_register_dispose(GArchRegister *reg) +static void g_arch_register_serializable_init(GSerializableObjectInterface *iface) { - G_OBJECT_CLASS(g_arch_register_parent_class)->dispose(G_OBJECT(reg)); + iface->load = NULL; + iface->store = NULL; } /****************************************************************************** * * -* Paramètres : reg = instance d'objet GLib à traiter. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Procède à la libération totale de la mémoire. * +* Description : Procède à l'initialisation de l'interface d'exportation. * * * * Retour : - * * * @@ -171,58 +169,55 @@ static void g_arch_register_dispose(GArchRegister *reg) * * ******************************************************************************/ -static void g_arch_register_finalize(GArchRegister *reg) +static void g_arch_register_string_builder_iface_init(GStringBuilderInterface *iface) { - G_OBJECT_CLASS(g_arch_register_parent_class)->finalize(G_OBJECT(reg)); + iface->to_string = NULL; } /****************************************************************************** * * -* Paramètres : reg = opérande à consulter pour le calcul. * +* Paramètres : reg = instance à initialiser. * * * -* Description : Produit une empreinte à partir d'un registre. * +* Description : Initialise une instance de registre. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -guint g_arch_register_hash(const GArchRegister *reg) +static void g_arch_register_init(GArchRegister *reg) { - return G_ARCH_REGISTER_GET_CLASS(reg)->hash(reg); } /****************************************************************************** * * -* Paramètres : a = premier registre à consulter. * -* b = second registre à consulter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Compare un registre avec un autre. * +* Description : Supprime toutes les références externes. * * * -* Retour : Bilan de la comparaison. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -int g_arch_register_compare(const GArchRegister *a, const GArchRegister *b) +static void g_arch_register_dispose(GObject *object) { - return G_ARCH_REGISTER_GET_CLASS(a)->compare(a, b); + G_OBJECT_CLASS(g_arch_register_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : reg = registre à transcrire. * -* line = ligne tampon où imprimer l'opérande donné. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Traduit un registre en version humainement lisible. * +* Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * @@ -230,9 +225,9 @@ int g_arch_register_compare(const GArchRegister *a, const GArchRegister *b) * * ******************************************************************************/ -void g_arch_register_print(const GArchRegister *reg, GBufferLine *line) +static void g_arch_register_finalize(GObject *object) { - G_ARCH_REGISTER_GET_CLASS(reg)->print(reg, line); + G_OBJECT_CLASS(g_arch_register_parent_class)->finalize(object); } @@ -287,115 +282,3 @@ bool g_arch_register_is_stack_pointer(const GArchRegister *reg) return result; } - - - -/* ---------------------------------------------------------------------------------- */ -/* CONSERVATION ET RECHARGEMENT DES DONNEES */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : reg = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * -* * -* Description : Charge un contenu depuis une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool _g_arch_register_load(GArchRegister *reg, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - - result = true; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : reg = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * -* * -* Description : Charge un contenu depuis une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_arch_register_load(GArchRegister *reg, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - GArchRegisterClass *class; /* Classe à activer */ - - class = G_ARCH_REGISTER_GET_CLASS(reg); - - result = class->load(reg, storage, pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : reg = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * -* * -* Description : Sauvegarde un contenu dans une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool _g_arch_register_store(GArchRegister *reg, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - - result = true; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : reg = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * -* * -* Description : Sauvegarde un contenu dans une mémoire tampon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_arch_register_store(GArchRegister *reg, GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - GArchRegisterClass *class; /* Classe à activer */ - - class = G_ARCH_REGISTER_GET_CLASS(reg); - - result = class->store(reg, storage, pbuf); - - return result; - -} diff --git a/src/arch/register.h b/src/arch/register.h index 0265a73..16275e0 100644 --- a/src/arch/register.h +++ b/src/arch/register.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * register.h - prototypes pour les aides auxiliaires relatives aux registres Dalvik * - * Copyright (C) 2012-2018 Cyrille Bagard + * Copyright (C) 2012-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,44 +25,19 @@ #define _ARCH_REGISTER_H -#include <glib-object.h> #include <stdbool.h> -#include "../glibext/bufferline.h" +#include "../glibext/helpers.h" -/* ---------------------------- PUR REGISTRE DU MATERIEL ---------------------------- */ +#define G_TYPE_ARCH_REGISTER (g_arch_register_get_type()) +DECLARE_GTYPE(GArchRegister, g_arch_register, G, ARCH_REGISTER); -#define G_TYPE_ARCH_REGISTER g_arch_register_get_type() -#define G_ARCH_REGISTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_ARCH_REGISTER, GArchRegister)) -#define G_IS_ARCH_REGISTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_ARCH_REGISTER)) -#define G_ARCH_REGISTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_ARCH_REGISTER, GArchRegisterClass)) -#define G_IS_ARCH_REGISTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_ARCH_REGISTER)) -#define G_ARCH_REGISTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_ARCH_REGISTER, GArchRegisterClass)) -/* Représentation d'un registre (instance) */ -typedef struct _GArchRegister GArchRegister; - -/* Représentation d'un registre (classe) */ -typedef struct _GArchRegisterClass GArchRegisterClass; - - -/* Indique le type défini pour une représentation d'un registre. */ -GType g_arch_register_get_type(void); - -/* Produit une empreinte à partir d'un registre. */ -guint g_arch_register_hash(const GArchRegister *); - -/* Compare un registre avec un autre. */ -int g_arch_register_compare(const GArchRegister *, const GArchRegister *); - -/* Traduit un registre en version humainement lisible. */ -void g_arch_register_print(const GArchRegister *, GBufferLine *); - /* Indique si le registre correspond à ebp ou similaire. */ bool g_arch_register_is_base_pointer(const GArchRegister *); diff --git a/src/arch/vmpa.h b/src/arch/vmpa.h index 42136e2..7a11deb 100644 --- a/src/arch/vmpa.h +++ b/src/arch/vmpa.h @@ -30,7 +30,6 @@ #include <stdint.h> -#include "archbase.h" #include "../common/cpp.h" #include "../common/datatypes.h" #include "../common/packed.h" @@ -297,4 +296,33 @@ char *mrange_length_to_string(const mrange_t *, MemoryDataSize, char [VMPA_MAX_L +/* ------------------- DEFINITION D'UN ESPACE RELATIVE EN MEMOIRE ------------------- */ + + +/* Couverture mémoire */ +typedef struct _rel_mrange_t +{ + uint32_t offset; /* Décalage depuis une ref. */ + uint16_t length; /* Taille de la couverture */ + +} rel_mrange_t; + + +#define init_rel_mrange(rr, o, l) \ + do \ + { \ + (rr)->offset = o; \ + (rr)->length = l; \ + } \ + while (0); + + +#define get_rel_mrange_offset(rr) \ + (rr)->offset + +#define get_rel_mrange_length(rr) \ + (rr)->length + + + #endif /* _ARCH_VMPA_H */ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 27ead1d..1056cb2 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -6,7 +6,6 @@ libcommon_la_SOURCES = \ array.h array.c \ asm.h asm.c \ bconst.h \ - bits.h bits.c \ compression.h compression.c \ cpp.h \ cpu.h cpu.c \ @@ -21,7 +20,6 @@ libcommon_la_SOURCES = \ packed.h packed.c \ pathname.h pathname.c \ pearson.h pearson.c \ - shuffle.h shuffle.c \ sort.h sort.c \ sqlite.h sqlite.c \ szstr.h \ @@ -49,11 +47,14 @@ endif libcommon4_la_SOURCES = \ + array.h array.c \ asm.h asm.c \ bits.h bits.c \ compiler.h \ + cpp.h \ datatypes.h \ dllist.h dllist.c \ + entropy.h entropy.c \ environment.h environment.c \ extstr.h extstr.c \ fnv1a.h fnv1a.c \ @@ -62,10 +63,38 @@ libcommon4_la_SOURCES = \ macros.h \ packed.h packed.c \ pathname.h pathname.c \ + shuffle.h shuffle.c \ sort.h sort.c \ + szbin.h \ xdg.h xdg.c -libcommon4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS) +if BUILD_CURL_SUPPORT + +libcommon4_la_SOURCES += \ + curl.h curl.c + +endif + +if BUILD_JSONGLIB_SUPPORT + +libcommon4_la_SOURCES += \ + json.h json.c + +endif + +libcommon4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS) $(LIBJSONGLIB_CFLAGS) + +if BUILD_CURL_SUPPORT + +libcommon4_la_CFLAGS += $(LIBCURL_CFLAGS) + +endif + +if BUILD_JSONGLIB_SUPPORT + +libcommon4_la_CFLAGS += $(LIBJSONGLIB_CFLAGS) + +endif devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) diff --git a/src/common/bits.c b/src/common/bits.c index 37e3141..27296f2 100644 --- a/src/common/bits.c +++ b/src/common/bits.c @@ -31,6 +31,8 @@ #include "asm.h" +#include "io.h" +#include "leb128.h" @@ -50,7 +52,8 @@ struct _bitfield_t /* Taille des mots intégrés */ -#define BF_WORD_SIZE (sizeof(unsigned long) * 8) +#define BF_WORD_BIT_SIZE (sizeof(unsigned long) * 8) +#define BF_WORD_BYTE_SIZE sizeof(unsigned long) /* Crée un champ de bits initialisé à zéro. */ @@ -82,10 +85,10 @@ static bitfield_t *_create_bit_field(size_t length) size_t needed; /* Nombre de mots à allouer */ size_t base; /* Allocation de base en octets*/ - needed = length / (sizeof(unsigned long) * 8); - if (length % (sizeof(unsigned long) * 8) != 0) needed++; + needed = length / BF_WORD_BIT_SIZE; + if (length % BF_WORD_BIT_SIZE != 0) needed++; - base = sizeof(bitfield_t) + needed * sizeof(unsigned long); + base = sizeof(bitfield_t) + needed * BF_WORD_BYTE_SIZE; result = malloc(base); @@ -148,7 +151,7 @@ bitfield_t *dup_bit_field(const bitfield_t *field) result = _create_bit_field(field->length); - memcpy(result->bits, field->bits, result->used_words * sizeof(unsigned long)); + memcpy(result->bits, field->bits, result->used_words * BF_WORD_BYTE_SIZE); return result; @@ -191,7 +194,7 @@ void copy_bit_field(bitfield_t *dest, const bitfield_t *src) { assert(dest->length == src->length); - memcpy(dest->bits, src->bits, dest->used_words * sizeof(unsigned long)); + memcpy(dest->bits, src->bits, dest->used_words * BF_WORD_BYTE_SIZE); } @@ -217,8 +220,8 @@ void truncate_bit_field(bitfield_t **field, size_t length) _field = *field; - needed = length / (sizeof(unsigned long) * 8); - if (length % (sizeof(unsigned long) * 8) != 0) needed++; + needed = length / BF_WORD_BIT_SIZE; + if (length % BF_WORD_BIT_SIZE != 0) needed++; if (needed <= _field->allocated_words) { @@ -263,10 +266,10 @@ void resize_bit_field(bitfield_t **field, size_t length) { /* Redimensionnement */ - needed = length / (sizeof(unsigned long) * 8); - if (length % (sizeof(unsigned long) * 8) != 0) needed++; + needed = length / BF_WORD_BIT_SIZE; + if (length % BF_WORD_BIT_SIZE != 0) needed++; - base = sizeof(bitfield_t) + needed * sizeof(unsigned long); + base = sizeof(bitfield_t) + needed * BF_WORD_BYTE_SIZE; /* Initialisation, si nécessaire */ @@ -275,8 +278,8 @@ void resize_bit_field(bitfield_t **field, size_t length) *field = realloc(_field, base); _field = *field; - last = _field->length / (sizeof(unsigned long) * 8); - remaining = _field->length % (sizeof(unsigned long) * 8); + last = _field->length / BF_WORD_BIT_SIZE; + remaining = _field->length % BF_WORD_BIT_SIZE; if (remaining != 0) { @@ -367,7 +370,7 @@ int compare_bit_fields(const bitfield_t *a, const bitfield_t *b) else { - final = a->length % (8 * sizeof(unsigned long)); + final = a->length % BF_WORD_BIT_SIZE; if (final == 0) final = ~0lu; @@ -414,7 +417,7 @@ int compare_bit_fields(const bitfield_t *a, const bitfield_t *b) void reset_all_in_bit_field(bitfield_t *field) { - memset(field->bits, 0u, field->used_words * sizeof(unsigned long)); + memset(field->bits, 0u, field->used_words * BF_WORD_BYTE_SIZE); } @@ -433,7 +436,35 @@ void reset_all_in_bit_field(bitfield_t *field) void set_all_in_bit_field(bitfield_t *field) { - memset(field->bits, ~0u, field->used_words * sizeof(unsigned long)); + memset(field->bits, ~0u, field->used_words * BF_WORD_BYTE_SIZE); + +} + + +/****************************************************************************** +* * +* Paramètres : field = champ de bits à modifier. * +* first = indice du premier bit à traiter. * +* * +* Description : Bascule à 0 une partie d'un champ de bits. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void reset_in_bit_field(bitfield_t *field, size_t first) +{ + size_t index; /* Cellule de tableau visée */ + size_t remaining; /* Nombre de bits restants */ + + assert(first < field->length); + + index = first / BF_WORD_BIT_SIZE; + remaining = first % BF_WORD_BIT_SIZE; + + field->bits[index] &= ~(1ul << remaining); } @@ -452,7 +483,7 @@ void set_all_in_bit_field(bitfield_t *field) * * ******************************************************************************/ -void reset_in_bit_field(bitfield_t *field, size_t first, size_t count) +void reset_multi_in_bit_field(bitfield_t *field, size_t first, size_t count) { size_t last; /* Point d'arrêt de la boucle */ size_t i; /* Boucle de parcours */ @@ -465,8 +496,8 @@ void reset_in_bit_field(bitfield_t *field, size_t first, size_t count) for (i = first; i < last; i++) { - index = i / (sizeof(unsigned long) * 8); - remaining = i % (sizeof(unsigned long) * 8); + index = i / BF_WORD_BIT_SIZE; + remaining = i % BF_WORD_BIT_SIZE; field->bits[index] &= ~(1ul << remaining); @@ -479,6 +510,34 @@ void reset_in_bit_field(bitfield_t *field, size_t first, size_t count) * * * Paramètres : field = champ de bits à modifier. * * first = indice du premier bit à traiter. * +* * +* Description : Bascule à 1 une partie d'un champ de bits. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void set_in_bit_field(bitfield_t *field, size_t first) +{ + size_t index; /* Cellule de tableau visée */ + size_t remaining; /* Nombre de bits restants */ + + assert(first < field->length); + + index = first / BF_WORD_BIT_SIZE; + remaining = first % BF_WORD_BIT_SIZE; + + field->bits[index] |= (1ul << remaining); + +} + + +/****************************************************************************** +* * +* Paramètres : field = champ de bits à modifier. * +* first = indice du premier bit à traiter. * * count = nombre de bits à marquer. * * * * Description : Bascule à 1 une partie d'un champ de bits. * @@ -489,7 +548,7 @@ void reset_in_bit_field(bitfield_t *field, size_t first, size_t count) * * ******************************************************************************/ -void set_in_bit_field(bitfield_t *field, size_t first, size_t count) +void set_multi_in_bit_field(bitfield_t *field, size_t first, size_t count) { size_t last; /* Point d'arrêt de la boucle */ size_t i; /* Boucle de parcours */ @@ -502,8 +561,8 @@ void set_in_bit_field(bitfield_t *field, size_t first, size_t count) for (i = first; i < last; i++) { - index = i / (sizeof(unsigned long) * 8); - remaining = i % (sizeof(unsigned long) * 8); + index = i / BF_WORD_BIT_SIZE; + remaining = i % BF_WORD_BIT_SIZE; field->bits[index] |= (1ul << remaining); @@ -587,12 +646,12 @@ void or_bit_field_at(bitfield_t *dest, const bitfield_t *src, size_t first) assert((first + src->length) <= dest->length); - start = first / (sizeof(unsigned long) * 8); - offset = first % (sizeof(unsigned long) * 8); + start = first / BF_WORD_BIT_SIZE; + offset = first % BF_WORD_BIT_SIZE; - remaining = (first + src->length) % (sizeof(unsigned long) * 8); + remaining = (first + src->length) % BF_WORD_BIT_SIZE; - if ((first + src->length) % (sizeof(unsigned long) * 8) > 0) + if ((first + src->length) % BF_WORD_BIT_SIZE > 0) last_iter = src->used_words; else last_iter = src->used_words - 1; @@ -605,7 +664,7 @@ void or_bit_field_at(bitfield_t *dest, const bitfield_t *src, size_t first) word = 0; if (i > 0 && offset > 0) - word |= src->bits[i - 1] >> (sizeof(unsigned long) * 8 - offset); + word |= src->bits[i - 1] >> (BF_WORD_BIT_SIZE - offset); if (i == last_iter && remaining > 0) word &= (1ul << remaining) - 1; @@ -638,8 +697,8 @@ bool test_in_bit_field(const bitfield_t *field, size_t n) assert(n < field->length); - index = n / (sizeof(unsigned long) * 8); - remaining = n % (sizeof(unsigned long) * 8); + index = n / BF_WORD_BIT_SIZE; + remaining = n % BF_WORD_BIT_SIZE; result = field->bits[index] & (1ul << remaining); @@ -670,8 +729,8 @@ bool test_and_set_in_bit_field(bitfield_t *field, size_t n) assert(n < field->length); - index = n / (sizeof(unsigned long) * 8); - remaining = n % (sizeof(unsigned long) * 8); + index = n / BF_WORD_BIT_SIZE; + remaining = n % BF_WORD_BIT_SIZE; bits = field->bits + index; @@ -713,8 +772,8 @@ static bool test_state_in_bit_field(const bitfield_t *field, size_t first, size_ for (i = first; i < last; i++) { - index = i / (sizeof(unsigned long) * 8); - remaining = i % (sizeof(unsigned long) * 8); + index = i / BF_WORD_BIT_SIZE; + remaining = i % BF_WORD_BIT_SIZE; current = field->bits[index] & (1ul << remaining); @@ -805,14 +864,30 @@ static bool test_state_within_bit_field(const bitfield_t *field, size_t first, c unsigned long bitmask; /* Masque à appliquer */ unsigned long test; /* Valeur résultante du test */ - result = true; + /** + * Si un masque est à appliquer avec débordement, les bits débordés sont considérés + * comme initialisés à la valeur par défaut. + */ + + if ((first + mask->length) > field->length) + { + assert(mask->length > 0); + + result = (state == field->default_state); + if (!result) goto done; + + if (first >= field->length) + goto done; - assert((first + mask->length) <= field->length); + } - start = first / (sizeof(unsigned long) * 8); - offset = first % (sizeof(unsigned long) * 8); + else + result = true; - remaining = mask->length % (sizeof(unsigned long) * 8); + start = first / BF_WORD_BIT_SIZE; + offset = first % BF_WORD_BIT_SIZE; + + remaining = mask->length % BF_WORD_BIT_SIZE; if (remaining == 0) finalcut = ~0lu; @@ -830,7 +905,7 @@ static bool test_state_within_bit_field(const bitfield_t *field, size_t first, c { word = field->bits[windex] >> offset; if ((windex + 1) < field->used_words) - word |= field->bits[windex + 1] << (sizeof(unsigned long) * 8 - offset); + word |= field->bits[windex + 1] << (BF_WORD_BIT_SIZE - offset); } bitmask = mask->bits[i]; @@ -849,6 +924,8 @@ static bool test_state_within_bit_field(const bitfield_t *field, size_t first, c } + done: + return result; } @@ -934,7 +1011,7 @@ size_t find_next_set_in_bit_field(const bitfield_t *field, const size_t *prev) } else { - i = *prev / BF_WORD_SIZE; + i = *prev / BF_WORD_BIT_SIZE; if (i >= field->used_words) { @@ -944,9 +1021,9 @@ size_t find_next_set_in_bit_field(const bitfield_t *field, const size_t *prev) word = field->bits[i]; - last_pos = *prev % BF_WORD_SIZE; + last_pos = *prev % BF_WORD_BIT_SIZE; - if ((last_pos + 1) == BF_WORD_SIZE) + if ((last_pos + 1) == BF_WORD_BIT_SIZE) goto next_word; word &= ~((1lu << (last_pos + 1)) - 1); @@ -957,7 +1034,7 @@ size_t find_next_set_in_bit_field(const bitfield_t *field, const size_t *prev) if (found > 0) { - result = i * BF_WORD_SIZE + found - 1; + result = i * BF_WORD_BIT_SIZE + found - 1; goto done; } @@ -973,7 +1050,7 @@ size_t find_next_set_in_bit_field(const bitfield_t *field, const size_t *prev) if (found > 0) { - result = i * BF_WORD_SIZE + found - 1; + result = i * BF_WORD_BIT_SIZE + found - 1; /** * Validation des bornes finales, pour le dernier mot. @@ -1148,7 +1225,7 @@ size_t popcount_for_bit_field(const bitfield_t *field) { value = field->bits[i]; - if (remaining < (8 * sizeof(unsigned long))) + if (remaining < BF_WORD_BIT_SIZE) value &= (1lu << remaining) - 1; #if __WORDSIZE == 64 @@ -1159,7 +1236,7 @@ size_t popcount_for_bit_field(const bitfield_t *field) # error "Unkown word size" #endif - remaining -= 8 * sizeof(unsigned long); + remaining -= BF_WORD_BIT_SIZE; } @@ -1190,8 +1267,6 @@ void output_bit_field(const bitfield_t *field) printf("[len=%zu] \n", field->length); -#define MAX_BITS (sizeof(unsigned long) * 8) - for (i = 0; i < field->used_words; i++) { value = field->bits[i]; @@ -1199,9 +1274,9 @@ void output_bit_field(const bitfield_t *field) if (i > 0) printf("| "); - for (k = 0; k < MAX_BITS; k++) + for (k = 0; k < BF_WORD_BIT_SIZE; k++) { - if ((i * MAX_BITS + k) >= field->length) + if ((i * BF_WORD_BIT_SIZE + k) >= field->length) break; printf("%c", value & (1lu << k) ? '1' : '.'); @@ -1218,3 +1293,133 @@ void output_bit_field(const bitfield_t *field) } #endif + + +/****************************************************************************** +* * +* Paramètres : fd = flux ouvert en lecture. * +* length = éventuelle indication de la taille du champ ? * +* def_state = éventuelle indication de l'état par défaut ? * +* endian = ordre des bits dans la source. * +* * +* Description : Restaure un champ de bits depuis un flux ouvert. * +* * +* Retour : Adresse du champs de bits mis en place, NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bitfield_t *load_bit_field(int fd, const size_t *length, const bool *def_state, SourceEndian endian) +{ + bitfield_t *result; /* Structure à retourner */ + size_t final_length; /* Nombre de bits représentés */ + uleb128_t leb128_value; /* Valeur LEB128 chargée */ + bool status; /* Bilan d'une lecture */ + bool final_default_state; /* Etat d'initialisation */ + uint8_t u8_value; /* Valeur sur 8 bits chargée */ + size_t i; /* Boucle de parcours */ + + result = NULL; + + if (length != NULL) + final_length = *length; + else + { + status = load_uleb128(&leb128_value, fd); + if (!status) goto exit; + + final_length = leb128_value; + + } + + if (def_state != NULL) + final_default_state = *def_state; + + else + { + status = load_u8(fd, &u8_value); + if (!status) goto exit; + + final_default_state = !!u8_value; + + } + + result = _create_bit_field(final_length); + + result->default_state = final_default_state; + + for (i = 0; i < result->used_words; i++) + { + status = load_uleb128(&leb128_value, fd); + + if (status) + result->bits[i] = leb128_value; + + else + { + delete_bit_field(result); + result = NULL; + break; + } + + } + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : field = champ de bits à consulter. * +* fd = flux ouvert en écriture. * +* skip_len = saute la sauvegarde de la taille du champ ? * +* skip_def = saute la sauvegarde de l'état par défaut ? * +* endian = ordre des bits dans la source. * +* * +* Description : Sauvegarde un champ de bits dans un flux ouvert. * +* * +* Retour : Bilan de l'opération : true en cas de succès, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool store_bit_field(const bitfield_t *field, int fd, bool skip_len, bool skip_def, SourceEndian endian) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + + if (skip_len) + result = true; + else + { + result = store_uleb128((const uleb128_t []) { field->length }, fd); + if (!result) goto exit; + } + + if (skip_def) + result = true; + else + { + result = store_u8(fd, field->default_state); + if (!result) goto exit; + } + + for (i = 0; i < field->used_words; i++) + { + result = store_uleb128((const uleb128_t []) { field->bits[i] }, fd); + + if (!result) + break; + + } + + exit: + + return result; + +} diff --git a/src/common/bits.h b/src/common/bits.h index a66c6f0..d9c83c8 100644 --- a/src/common/bits.h +++ b/src/common/bits.h @@ -29,6 +29,9 @@ #include <sys/types.h> +#include "datatypes.h" + + /* Champ de bits simple */ typedef struct _bitfield_t bitfield_t; @@ -65,10 +68,16 @@ void reset_all_in_bit_field(bitfield_t *); void set_all_in_bit_field(bitfield_t *); /* Bascule à 0 une partie d'un champ de bits. */ -void reset_in_bit_field(bitfield_t *, size_t, size_t); +void reset_in_bit_field(bitfield_t *, size_t); + +/* Bascule à 0 une partie d'un champ de bits. */ +void reset_multi_in_bit_field(bitfield_t *, size_t, size_t); /* Bascule à 1 une partie d'un champ de bits. */ -void set_in_bit_field(bitfield_t *, size_t, size_t); +void set_in_bit_field(bitfield_t *, size_t); + +/* Bascule à 1 une partie d'un champ de bits. */ +void set_multi_in_bit_field(bitfield_t *, size_t, size_t); /* Réalise une opération ET logique entre deux champs de bits. */ void and_bit_field(bitfield_t *, const bitfield_t *); @@ -113,6 +122,12 @@ void output_bit_field(const bitfield_t *); #endif +/* Restaure un champ de bits depuis un flux ouvert. */ +bitfield_t *load_bit_field(int, const size_t *, const bool *, SourceEndian); + +/* Sauvegarde un champ de bits dans un flux ouvert. */ +bool store_bit_field(const bitfield_t *, int, bool, bool, SourceEndian); + #endif /* _COMMON_BITS_H */ diff --git a/src/common/compiler.h b/src/common/compiler.h index 2585e47..65df8a4 100644 --- a/src/common/compiler.h +++ b/src/common/compiler.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * compiler.h - prototypes pour le regroupement d'astuces à destination du compilateur * - * Copyright (C) 2024 Cyrille Bagard + * Copyright (C) 2024-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -34,5 +34,19 @@ #define __weak __attribute__((weak)) +/** + * Contournement des avertissements de la forme suivante : + * + * assignment to 'const char * const*' from incompatible pointer type 'char **' [-Wincompatible-pointer-types] + * + * Références : + * - https://www.reddit.com/r/C_Programming/comments/qa2231/const_char_const_and_char_are_incompatible/ + * - https://stackoverflow.com/questions/78125/why-cant-i-convert-char-to-a-const-char-const-in-c + * - https://c-faq.com/ansi/constmismatch.html + */ + +#define CONST_ARRAY_CAST(a, tp) (const tp **)a + + #endif /* _COMMON_COMPILER_H */ diff --git a/src/common/cpp.h b/src/common/cpp.h index 39e7676..4ebad82 100644 --- a/src/common/cpp.h +++ b/src/common/cpp.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * cpp.h - prototypes pour avoir à disposition un langage C plus plus mieux * - * Copyright (C) 2010-2020 Cyrille Bagard + * Copyright (C) 2010-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -31,12 +31,27 @@ /** + * Fournit la taille d'une chaîne statique. + */ +#define STATIC_STR_SIZE(s) (sizeof(s) - 1) + + +/** * Fournit la taille d'un tableau statique. */ #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) /** + * Facilite la transmission d'arguement pour des fonctions + * comme strncmp() et Cie. + */ +#define SL(str) str, strlen(str) + +#define STCSL(str) str, STATIC_STR_SIZE(str) + + +/** * Détermine la taille de la plus longue chaîne de caractères * correspondant à un type donné. */ diff --git a/src/common/curl.c b/src/common/curl.c index 573180f..e9fb92f 100644 --- a/src/common/curl.c +++ b/src/common/curl.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * curl.c - encapsulation des fonctionnalités de cURL * - * Copyright (C) 2022 Cyrille Bagard + * Copyright (C) 2022-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,14 +28,62 @@ #include <string.h> +#include "../core/global.h" + + + +/* Mémorise un volume de données transférées. */ +static int track_curl_data_transfers(CURL *, curl_infotype, char *, size_t, void *); /* Mémorise les données reçues en réponse à une requête. */ -static size_t receive_data_from_internet(void *, size_t, size_t, curl_net_data_t *); +static size_t receive_data_from_internet(void *, size_t, size_t, sized_binary_t *); /****************************************************************************** * * +* Paramètres : handle = gestionnaire de la requête cURL concernée. * +* type = type de transfert réalisé. * +* data = pointeur vers les données manipulées. * +* size = quantité de données transférées. * +* unused = adresse non utilisée ici. * +* * +* Description : Mémorise un volume de données transférées. * +* * +* Retour : CURLE_OK. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int track_curl_data_transfers(CURL *handle, curl_infotype type, char *data, size_t size, void *unused) +{ + switch (type) + { + case CURLINFO_HEADER_IN: + case CURLINFO_DATA_IN: + /*case CURLINFO_SSL_DATA_IN:*/ + update_network_stats(size, 0); + break; + + case CURLINFO_HEADER_OUT: + case CURLINFO_DATA_OUT: + /*case CURLINFO_SSL_DATA_OUT:*/ + update_network_stats(0, size); + break; + + default: + break; + + } + + return 0; + +} + + +/****************************************************************************** +* * * Paramètres : contents = contenu nouveau en arrivance d'Internet. * * size = taille d'un élément reçu. * * nmemb = quantité de ces éléments. * @@ -49,19 +97,19 @@ static size_t receive_data_from_internet(void *, size_t, size_t, curl_net_data_t * * ******************************************************************************/ -static size_t receive_data_from_internet(void *contents, size_t size, size_t nmemb, curl_net_data_t *data) +static size_t receive_data_from_internet(void *contents, size_t size, size_t nmemb, sized_binary_t *data) { size_t realsize; /* Taille brute en octets */ realsize = size * nmemb; - data->memory = realloc(data->memory, data->size + realsize + 1); + data->data = realloc(data->data, data->size + realsize + 1); - memcpy(&(data->memory[data->size]), contents, realsize); + memcpy(&(data->data[data->size]), contents, realsize); data->size += realsize; - data->memory[data->size] = 0; + data->data[data->size] = 0; return realsize; @@ -85,7 +133,7 @@ static size_t receive_data_from_internet(void *contents, size_t size, size_t nme * * ******************************************************************************/ -bool send_http_get_request(const char *url, char * const headers[], size_t hcount, const char *cookies, setup_extra_curl_cb ecb, curl_net_data_t *resp) +bool send_http_get_request(const char *url, char * const headers[], size_t hcount, const char *cookies, setup_extra_curl_cb ecb, sized_binary_t *resp) { bool result; /* Bilan d'opération à renvoyer*/ CURL *req; /* Requête HTTP */ @@ -95,12 +143,14 @@ bool send_http_get_request(const char *url, char * const headers[], size_t hcoun result = false; - resp->memory = NULL; - resp->size = 0; + init_sized_binary(resp); req = curl_easy_init(); if (req == NULL) goto exit; + curl_easy_setopt(req, CURLOPT_DEBUGFUNCTION, track_curl_data_transfers); + curl_easy_setopt(req, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(req, CURLOPT_URL, url); /* Entêtes à transmettre */ @@ -165,7 +215,7 @@ bool send_http_get_request(const char *url, char * const headers[], size_t hcoun * * ******************************************************************************/ -bool send_http_post_request(const char *url, char * const headers[], size_t hcount, const char *cookies, const curl_net_data_t *payload, setup_extra_curl_cb ecb, curl_net_data_t *resp) +bool send_http_post_request(const char *url, char * const headers[], size_t hcount, const char *cookies, const sized_binary_t *payload, setup_extra_curl_cb ecb, sized_binary_t *resp) { bool result; /* Bilan d'opération à renvoyer*/ CURL *req; /* Requête HTTP */ @@ -175,12 +225,14 @@ bool send_http_post_request(const char *url, char * const headers[], size_t hcou result = false; - resp->memory = NULL; - resp->size = 0; + init_sized_binary(resp); req = curl_easy_init(); if (req == NULL) goto exit; + curl_easy_setopt(req, CURLOPT_DEBUGFUNCTION, track_curl_data_transfers); + curl_easy_setopt(req, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(req, CURLOPT_URL, url); /* Entêtes à transmettre */ @@ -205,7 +257,7 @@ bool send_http_post_request(const char *url, char * const headers[], size_t hcou curl_easy_setopt(req, CURLOPT_POST, 1); - curl_easy_setopt(req, CURLOPT_POSTFIELDS, payload->memory); + curl_easy_setopt(req, CURLOPT_POSTFIELDS, payload->data); curl_easy_setopt(req, CURLOPT_POSTFIELDSIZE, payload->size); /* Emission de la requête */ diff --git a/src/common/curl.h b/src/common/curl.h index 02d9e91..1fc8f54 100644 --- a/src/common/curl.h +++ b/src/common/curl.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * curl.h - prototypes pour l'encapsulation des fonctionnalités de cURL * - * Copyright (C) 2022 Cyrille Bagard + * Copyright (C) 2022-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -29,14 +29,8 @@ #include <curl/curl.h> +#include "szbin.h" -/* Données échangées avec Internet */ -typedef struct _curl_net_data_t -{ - char *memory; /* Zone de mémoire allouée */ - size_t size; /* Quantité de données */ - -} curl_net_data_t; /* Prototype pour une intervention complémentaire dans la préparation des requêtes */ @@ -44,10 +38,10 @@ typedef CURLcode (* setup_extra_curl_cb) (CURL *); /* Mémorise les données reçues en réponse à une requête. */ -bool send_http_get_request(const char *, char * const [], size_t, const char *, setup_extra_curl_cb, curl_net_data_t *); +bool send_http_get_request(const char *, char * const [], size_t, const char *, setup_extra_curl_cb, sized_binary_t *); /* Mémorise les données reçues en réponse à une requête. */ -bool send_http_post_request(const char *, char * const [], size_t, const char *, const curl_net_data_t *, setup_extra_curl_cb, curl_net_data_t *); +bool send_http_post_request(const char *, char * const [], size_t, const char *, const sized_binary_t *, setup_extra_curl_cb, sized_binary_t *); diff --git a/src/common/datatypes.h b/src/common/datatypes.h index 3983267..248f4a1 100644 --- a/src/common/datatypes.h +++ b/src/common/datatypes.h @@ -52,5 +52,35 @@ typedef enum _SourceEndian } SourceEndian; +/* Taille des données intégrées */ +typedef enum _MemoryDataSize +{ + MDS_UNDEFINED = 0x0, /* Taille non définie */ + + MDS_4_BITS_UNSIGNED = 0x1, /* Opérande sur 4 bits n.-s. */ + MDS_8_BITS_UNSIGNED = 0x2, /* Opérande sur 8 bits n.-s. */ + MDS_16_BITS_UNSIGNED = 0x3, /* Opérande sur 16 bits n.-s. */ + MDS_32_BITS_UNSIGNED = 0x4, /* Opérande sur 32 bits n.-s. */ + MDS_64_BITS_UNSIGNED = 0x5, /* Opérande sur 64 bits n.-s. */ + + MDS_4_BITS_SIGNED = 0x8 | 0x1, /* Opérande sur 4 bits signés */ + MDS_8_BITS_SIGNED = 0x8 | 0x2, /* Opérande sur 8 bits signés */ + MDS_16_BITS_SIGNED = 0x8 | 0x3, /* Opérande sur 16 bits signés */ + MDS_32_BITS_SIGNED = 0x8 | 0x4, /* Opérande sur 32 bits signés */ + MDS_64_BITS_SIGNED = 0x8 | 0x5 /* Opérande sur 64 bits signés */ + +} MemoryDataSize; + +#define MDS_RANGE(mds) ((mds & 0x7) - 1) +#define MDS_SIGN 0x8 +#define MDS_IS_SIGNED(mds) (mds & MDS_SIGN) + +#define MDS_4_BITS MDS_4_BITS_UNSIGNED +#define MDS_8_BITS MDS_8_BITS_UNSIGNED +#define MDS_16_BITS MDS_16_BITS_UNSIGNED +#define MDS_32_BITS MDS_32_BITS_UNSIGNED +#define MDS_64_BITS MDS_64_BITS_UNSIGNED + + #endif /* _COMMON_DATATYPES_H */ diff --git a/src/common/entropy.c b/src/common/entropy.c new file mode 100644 index 0000000..8dae698 --- /dev/null +++ b/src/common/entropy.c @@ -0,0 +1,88 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * entropy.c - calcul de l'entropie d'un contenu binaire + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "entropy.h" + + +#include <math.h> +#include <string.h> + + + +/****************************************************************************** +* * +* Paramètres : data = séquence d'octets à traiter. * +* len = quantité de ces octets. * +* bits = calcul en concidérant les bits et non les octets ? * +* * +* Description : Détermine l'entropie d'un contenu binaire. * +* * +* Retour : Valeur d'entropie du contenu fourni. * +* * +* Remarques : - * +* * +******************************************************************************/ + +double compute_entropy(const bin_t *data, size_t len, bool bits) +{ + double result; /* Valeur calculée à renvoyer */ + unsigned long counters[256]; /* Décompte des valeurs */ + const bin_t *d_max; /* Borne de fin de parcours #1 */ + const bin_t *d_iter; /* Boucle de parcours #1 */ + double log_2; /* Valeur constante de log2 */ + unsigned long *c_max; /* Borne de fin de parcours #2 */ + unsigned long *c_iter; /* Boucle de parcours #2 */ + double freq; /* Fréquence liée à une valeur */ + + result = 0.0; + + memset(counters, 0, sizeof(counters)); + + d_max = data + len; + + for (d_iter = data; d_iter < d_max; d_iter++) + counters[*d_iter]++; + + /** + * Explication du choix de log : + * https://stackoverflow.com/questions/990477/how-to-calculate-the-entropy-of-a-file/990646#990646 + */ + + log_2 = log(bits ? 2.0 : 256.0); + + c_max = counters + 256; + + for (c_iter = counters; c_iter < c_max; c_iter++) + { + if (*c_iter == 0lu) + continue; + + freq = ((double)*c_iter) / ((double)len); + + result -= freq * (log(freq) / log_2); + + } + + return result; + +} diff --git a/plugins/pe/section.h b/src/common/entropy.h index 2ac21d3..cfd51ef 100644 --- a/plugins/pe/section.h +++ b/src/common/entropy.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * section.h - prototypes pour la gestion des sections d'un PE + * entropy.h - prototypes pour le calcul de l'entropie d'un contenu binaire * - * Copyright (C) 2010-2017 Cyrille Bagard + * Copyright (C) 2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,18 +21,21 @@ */ -#ifndef _PLUGINS_PE_SECTION_H -#define _PLUGINS_PE_SECTION_H +#ifndef _COMMON_ENTROPY_H +#define _COMMON_ENTROPY_H -#include "format.h" -#include "pe_def.h" +#include <stdbool.h> +#include <stddef.h> +#include "../common/datatypes.h" -/* Recherche une section donnée au sein de binaire par indice. */ -image_section_header *read_all_pe_sections(const GPeFormat *, vmpa2t *); +/* Détermine l'entropie d'un contenu binaire. */ +double compute_entropy(const bin_t *, size_t, bool); -#endif /* _PLUGINS_PE_SECTION_H */ + + +#endif /* _COMMON_ENTROPY_H */ diff --git a/src/common/extstr.c b/src/common/extstr.c index bd3491f..fe0bab4 100644 --- a/src/common/extstr.c +++ b/src/common/extstr.c @@ -636,7 +636,7 @@ const void *memcasemem(const void *haystack, size_t haystacklen, const void *nee * * * Description : Compare sans casse deux série d'octets entre elles. * * * -* Retour : Status de la comparaison des séries d'octets. * +* Retour : Statut de la comparaison des séries d'octets. * * * * Remarques : - * * * diff --git a/src/common/fnv1a.c b/src/common/fnv1a.c index a8afd0a..057d6c9 100644 --- a/src/common/fnv1a.c +++ b/src/common/fnv1a.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * fnv1a.c - implémentaton du calcul rapide d'empreintes de chaînes * - * Copyright (C) 2012-2018 Cyrille Bagard + * Copyright (C) 2012-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -87,3 +87,34 @@ fnv64_t fnv_64a_hash(const char *str) return result; } + + +/****************************************************************************** +* * +* Paramètres : data = données binaires à traiter. * +* size = quantité de ces données. * +* * +* Description : Détermine l'empreinte FNV1a d'une séquence d'octets. * +* * +* Retour : Valeur calculée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +fnv64_t fnv_64a_hash_data(const uint8_t *data, size_t size) +{ + fnv64_t result; /* Valeur à retourner */ + size_t i; /* Boucle de parcours */ + + result = FNV1A_64_INIT; + + for (i = 0; i < size; i++) + { + result ^= (fnv64_t)data[i]; + result *= FNV_64_PRIME; + } + + return result; + +} diff --git a/src/common/fnv1a.h b/src/common/fnv1a.h index eec1460..4da3108 100644 --- a/src/common/fnv1a.h +++ b/src/common/fnv1a.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * fnv1a.h - prototypes pour l'implémentaton du calcul rapide d'empreintes de chaînes * - * Copyright (C) 2012-2018 Cyrille Bagard + * Copyright (C) 2012-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -27,6 +27,7 @@ #include <stdbool.h> #include <stdint.h> +#include <sys/types.h> /** @@ -46,6 +47,9 @@ int cmp_fnv_64a(fnv64_t, fnv64_t); /* Détermine l'empreinte FNV1a d'une chaîne de caractères. */ fnv64_t fnv_64a_hash(const char *); +/* Détermine l'empreinte FNV1a d'une séquence d'octets. */ +fnv64_t fnv_64a_hash_data(const uint8_t *, size_t); + #endif /* _COMMON_FNV1A_H */ diff --git a/src/common/io.h b/src/common/io.h index 7fe9d9d..1932b12 100644 --- a/src/common/io.h +++ b/src/common/io.h @@ -39,12 +39,69 @@ /* Lit des données depuis un flux local. */ bool safe_read(int, void *, size_t); +#define load_u8(f, v) \ + safe_read(f, v, sizeof(uint8_t)); + +#define load_u16(f, v, e) \ + ({ \ + bool __ret; \ + uint16_t __val; \ + __ret = safe_read(f, &__val, sizeof(uint16_t)); \ + __val = swap_u16(&__val, e); \ + *(v) = __val; \ + __ret; \ + }) + +#define load_u32(f, v, e) \ + ({ \ + bool __ret; \ + uint32_t __val; \ + __ret = safe_read(f, &__val, sizeof(uint32_t)); \ + __val = swap_u32(&__val, e); \ + *(v) = __val; \ + __ret; \ + }) + +#define load_u64(f, v, e) \ + ({ \ + bool __ret; \ + uint64_t __val; \ + __ret = safe_read(f, &__val, sizeof(uint64_t)); \ + __val = swap_u64(&__val, e); \ + *(v) = __val; \ + __ret; \ + }) + /* Lit des données depuis un flux local. */ ssize_t safe_read_partial(int, void *, size_t); /* Ecrit des données dans un flux local. */ bool safe_write(int, const void *, size_t); +#define store_u8(f, v) \ + safe_write(f, (const uint8_t []){ v }, sizeof(uint8_t)); + +#define store_u16(f, v, e) \ + ({ \ + uint16_t __val; \ + __val = swap_u16((const uint16_t []){ v }, e); \ + safe_write(f, &__val, sizeof(uint16_t)); \ + }) + +#define store_u32(f, v, e) \ + ({ \ + uint32_t __val; \ + __val = swap_u32((const uint32_t []){ v }, e); \ + safe_write(f, &__val, sizeof(uint32_t)); \ + }) + +#define store_u64(f, v, e) \ + ({ \ + uint64_t __val; \ + __val = swap_u64((const uint64_t []){ v }, e); \ + safe_write(f, &__val, sizeof(uint64_t)); \ + }) + /* Réceptionne des données depuis un flux réseau. */ bool safe_recv(int, void *, size_t, int); diff --git a/src/common/json.c b/src/common/json.c new file mode 100644 index 0000000..cb64822 --- /dev/null +++ b/src/common/json.c @@ -0,0 +1,203 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * json.c - manipulations génériques de données au format JSON + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "json.h" + + +#include <assert.h> + + + +/****************************************************************************** +* * +* Paramètres : root = racine d'une arborescence JSON chargée. * +* query = chemin XPath d'une requête à mener. * +* error = description d'une éventuelle erreur rencontrée. [OUT]* +* * +* Description : Détermine la présence d'un élément avec une valeur textuelle.* +* * +* Retour : true si le noeud existe et porte une chaîne comme valeur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool has_json_string_value(JsonNode *root, const char *xpath, GError **error) +{ + bool result; /* Bilan à retourner */ + JsonNode *found; /* Accès direct aux trouvailles*/ + JsonArray *array; /* Liste des résultats */ + guint count; /* Taille de la liste */ + JsonNode *node; /* Noeud portant la cible */ + GValue value; /* Valeur correspondante */ + + result = false; + + *error = NULL; + found = json_path_query(xpath, root, error); + + if (found != NULL) + { + assert(*error == NULL); + + array = json_node_get_array(found); + + count = json_array_get_length(array); + + if (count == 1) + { + node = json_array_get_element(array, 0); + + memset(&value, 0, sizeof(GValue)); + json_node_get_value(node, &value); + + if (G_VALUE_HOLDS(&value, G_TYPE_STRING)) + result = true; + + } + + json_node_unref(found); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : root = racine d'une arborescence JSON chargée. * +* query = chemin XPath d'une requête à mener. * +* error = description d'une éventuelle erreur rencontrée. [OUT]* +* * +* Description : Fournit le contenu d'un élément avec une valeur textuelle. * +* * +* Retour : Valeur trouvée ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *get_json_string_value(JsonNode *root, const char *xpath, GError **error) +{ + char *result; /* Valeur à retourner */ + JsonNode *found; /* Accès direct aux trouvailles*/ + JsonArray *array; /* Liste des résultats */ + guint count; /* Taille de la liste */ + JsonNode *node; /* Noeud portant la cible */ + GValue value; /* Valeur correspondante */ + const gchar *raw; /* Valeur brute présente */ + + result = NULL; + + *error = NULL; + found = json_path_query(xpath, root, error); + + if (found != NULL) + { + assert(*error == NULL); + + array = json_node_get_array(found); + + count = json_array_get_length(array); + + if (count == 1) + { + node = json_array_get_element(array, 0); + + memset(&value, 0, sizeof(GValue)); + json_node_get_value(node, &value); + + if (G_VALUE_HOLDS(&value, G_TYPE_STRING)) + { + raw = json_array_get_string_element(array, 0); + result = strdup(raw); + } + + } + + json_node_unref(found); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : root = racine d'une arborescence JSON chargée. * +* query = chemin XPath d'une requête à mener. * +* error = description d'une éventuelle erreur rencontrée. [OUT]* +* * +* Description : Fournit le contenu d'un élément avec une valeur entière. * +* * +* Retour : Valeur trouvée ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +long long get_json_integer_value(JsonNode *root, const char *xpath, GError **error) +{ + long long result; /* Valeur à retourner */ + JsonNode *found; /* Accès direct aux trouvailles*/ + JsonArray *array; /* Liste des résultats */ + guint count; /* Taille de la liste */ + JsonNode *node; /* Noeud portant la cible */ + GValue value; /* Valeur correspondante */ + + result = 0xffffffffffffffffll; + + *error = NULL; + found = json_path_query(xpath, root, error); + + if (found != NULL) + { + assert(*error == NULL); + + array = json_node_get_array(found); + + count = json_array_get_length(array); + + if (count == 1) + { + node = json_array_get_element(array, 0); + + memset(&value, 0, sizeof(GValue)); + json_node_get_value(node, &value); + + if (G_VALUE_HOLDS(&value, G_TYPE_INT64)) + result = json_array_get_int_element(array, 0); + + } + + json_node_unref(found); + + } + + return result; + +} diff --git a/src/common/json.h b/src/common/json.h new file mode 100644 index 0000000..8794bf6 --- /dev/null +++ b/src/common/json.h @@ -0,0 +1,44 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * json.h - prototypes pour des manipulations génériques de données au format JSON + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _COMMON_JSON_H +#define _COMMON_JSON_H + + +#include <stdbool.h> +#include <json-glib/json-glib.h> + + + +/* Détermine la présence d'un élément avec une valeur textuelle. */ +bool has_json_string_value(JsonNode *, const char *, GError **); + +/* Fournit le contenu d'un élément avec une valeur textuelle. */ +char *get_json_string_value(JsonNode *, const char *, GError **); + +/* Fournit le contenu d'un élément avec une valeur entière. */ +long long get_json_integer_value(JsonNode *, const char *, GError **); + + + +#endif /* _COMMON_JSON_H */ diff --git a/src/common/leb128.c b/src/common/leb128.c index c8d2ace..7fae4d0 100644 --- a/src/common/leb128.c +++ b/src/common/leb128.c @@ -24,6 +24,20 @@ #include "leb128.h" +#include <malloc.h> + + +#include "io.h" + + +/** + * Quantité maximale d'octets de représentation. + * + * sizeof([u]leb128_t) / 7 = 9.142857142857142 + * + */ +#define MAX_LEB128_BYTES 9 + /****************************************************************************** * * @@ -113,10 +127,63 @@ bool read_leb128(leb128_t *target, const bin_t *data, phys_t *pos, phys_t len) /****************************************************************************** * * +* Paramètres : value = valeur à constituer. [OUT] * +* fd = flux ouvert en lecture à consulter. * +* * +* Description : Restaure un nombre non signé encodé au format LEB128. * +* * +* Retour : Bilan de l'opération : true en cas de succès, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool load_uleb128(uleb128_t *value, int fd) +{ + bool result; /* Bilan à retourner */ + unsigned int shift; /* Décalage à appliquer */ + uint8_t byte; /* Octet à transposer */ + + result = false; + + *value = 0; + + shift = 0; + + while (true) + { + /* Encodage sur trop d'octets ? */ + if (shift > (7 * MAX_LEB128_BYTES)) + { + result = false; + break; + } + + result = safe_read(fd, &byte, sizeof(uint8_t)); + if (!result) break; + + *value |= ((byte & 0x7f) << shift); + + result = true; + + if ((byte & 0x80) == 0x00) + break; + + shift += 7; + + } + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : value = valeur à consigner. * -* pbuf = tampon de données à constituer. [OUT] * +* fd = flux ouvert en écriture. * * * -* Description : Encode un nombre non signé encodé au format LEB128. * +* Description : Sauvegarde un nombre non signé encodé au format LEB128. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * @@ -124,7 +191,7 @@ bool read_leb128(leb128_t *target, const bin_t *data, phys_t *pos, phys_t len) * * ******************************************************************************/ -bool pack_uleb128(const uleb128_t *value, packed_buffer_t *pbuf) +bool store_uleb128(const uleb128_t *value, int fd) { bool result; /* Bilan à retourner */ uleb128_t tmp; /* Valeur modifiable */ @@ -140,7 +207,7 @@ bool pack_uleb128(const uleb128_t *value, packed_buffer_t *pbuf) if (tmp != 0) byte |= 0x80; - result = extend_packed_buffer(pbuf, &byte, sizeof(uint8_t), false); + result = safe_write(fd, &byte, sizeof(uint8_t)); } while (result && tmp != 0); @@ -153,9 +220,9 @@ bool pack_uleb128(const uleb128_t *value, packed_buffer_t *pbuf) /****************************************************************************** * * * Paramètres : value = valeur à consigner. * -* pbuf = tampon de données à constituer. [OUT] * +* len = taille du tampon de données à constitué. [OUT] * * * -* Description : Encode un nombre signé encodé au format LEB128. * +* Description : Encode un nombre non signé encodé au format LEB128. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * @@ -163,19 +230,81 @@ bool pack_uleb128(const uleb128_t *value, packed_buffer_t *pbuf) * * ******************************************************************************/ -bool pack_leb128(const leb128_t *value, packed_buffer_t *pbuf) +void *pack_uleb128(const uleb128_t *value, size_t *len) { + uint8_t *result; /* Données à retourner */ + uleb128_t tmp; /* Valeur modifiable */ + uint8_t *byte; /* Octet à transposer */ - bool result; /* Bilan à retourner */ + /* Calcul de la quantité d'octets nécessaires */ + + *len = 0; + + tmp = *value; + + do + { + tmp >>= 7; + (*len)++; + } + while (tmp != 0); + + /* Exportation */ + + result = malloc(*len * sizeof(uint8_t)); + byte = result; + + tmp = *value; + + do + { + *byte = (tmp & 0x7f); + tmp >>= 7; + + if (tmp != 0) + *byte |= 0x80; + + byte++; + + } + while (tmp != 0); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : value = valeur à consigner. * +* len = taille du tampon de données à constitué. [OUT] * +* * +* Description : Encode un nombre signé encodé au format LEB128. * +* * +* Retour : Bilan de l'opération : true en cas de succès, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +void *pack_leb128(const leb128_t *value, size_t *len) +{ + uint8_t *result; /* Données à retourner */ + bool negative; /* Nature de la valeur */ uleb128_t tmp; /* Valeur modifiable */ bool more; /* Poursuite des traitements */ - bool negative; /* Nature de la valeur */ uint8_t byte; /* Octet à transposer */ + uint8_t *iter; /* Boucle de parcours */ + + negative = (*value < 0); + + /* Calcul de la quantité d'octets nécessaires */ + + *len = 0; tmp = *value; more = true; - negative = (*value < 0); while (more) { @@ -198,10 +327,44 @@ bool pack_leb128(const leb128_t *value, packed_buffer_t *pbuf) if ((tmp == 0 && (byte & 0x40) == 0x00) || (tmp == -1 && (byte & 0x40) == 0x40)) more = false; + (*len)++; + + } + + /* Exportation */ + + result = malloc(*len * sizeof(uint8_t)); + iter = result; + + tmp = *value; + + more = true; + + while (more) + { + *iter = (tmp & 0x7f); + tmp >>= 7; + + /** + * Propagation forcée du bit de signe pour les implémentations de + * décalage basées sur une opération logique et non arithmétique. + */ + + if (negative) + tmp |= (~0llu << (LEB128_BITS_COUNT - 7)); + + /** + * Le bit de signe n'est pas le bit de poids fort ici : + * On travaille sur 7 bits, donc le masque est 0x40 ! + */ + + if ((tmp == 0 && (*iter & 0x40) == 0x00) || (tmp == -1 && (*iter & 0x40) == 0x40)) + more = false; + else - byte |= 0x80; + *iter |= 0x80; - result = extend_packed_buffer(pbuf, &byte, sizeof(uint8_t), false); + iter++; } @@ -213,7 +376,8 @@ bool pack_leb128(const leb128_t *value, packed_buffer_t *pbuf) /****************************************************************************** * * * Paramètres : value = valeur à constituer. [OUT] * -* pbuf = tampon de données à consulter. * +* pos = tête de lecture à faire évoluer. [OUT] * +* max = position maximale liée à la fin des données. * * * * Description : Décode un nombre non signé encodé au format LEB128. * * * @@ -223,38 +387,45 @@ bool pack_leb128(const leb128_t *value, packed_buffer_t *pbuf) * * ******************************************************************************/ -bool unpack_uleb128(uleb128_t *value, packed_buffer_t *pbuf) +bool unpack_uleb128(uleb128_t *value, const void **pos, const void *max) { bool result; /* Bilan à retourner */ unsigned int shift; /* Décalage à appliquer */ - uint8_t byte; /* Octet à transposer */ + uint8_t *byte; /* Octet à transposer */ - result = true; + result = false; *value = 0; shift = 0; + byte = *(uint8_t **)pos; - while (true) + do { /* Encodage sur trop d'octets ? */ - if (shift > (7 * sizeof(uleb128_t))) + if (shift > (7 * MAX_LEB128_BYTES)) { result = false; break; } - result = extract_packed_buffer(pbuf, &byte, sizeof(uint8_t), false); - if (!result) break; + /* Atteinte de la fin des données ? */ + if ((void *)byte >= max) + { + result = false; + break; + } - *value |= ((byte & 0x7f) << shift); + *value |= ((*byte & 0x7f) << shift); - if ((byte & 0x80) == 0x00) - break; + result = true; shift += 7; } + while ((*byte++ & 0x80) == 0x80); + + *pos = byte; return result; @@ -264,7 +435,8 @@ bool unpack_uleb128(uleb128_t *value, packed_buffer_t *pbuf) /****************************************************************************** * * * Paramètres : value = valeur à constituer. [OUT] * -* pbuf = tampon de données à consulter. * +* pos = tête de lecture à faire évoluer. [OUT] * +* max = position maximale liée à la fin des données. * * * * Description : Décode un nombre signé encodé au format LEB128. * * * @@ -274,44 +446,56 @@ bool unpack_uleb128(uleb128_t *value, packed_buffer_t *pbuf) * * ******************************************************************************/ -bool unpack_leb128(leb128_t *value, packed_buffer_t *pbuf) +bool unpack_leb128(leb128_t *value, const void **pos, const void *max) { bool result; /* Bilan à retourner */ unsigned int shift; /* Décalage à appliquer */ - uint8_t byte; /* Octet à transposer */ + uint8_t *byte; /* Octet à transposer */ - result = true; + result = false; *value = 0; shift = 0; + byte = *(uint8_t **)pos; do { /* Encodage sur trop d'octets ? */ - if (shift > (7 * sizeof(leb128_t))) + if (shift > (7 * MAX_LEB128_BYTES)) { result = false; break; } - result = extract_packed_buffer(pbuf, &byte, sizeof(uint8_t), false); - if (!result) break; + /* Atteinte de la fin des données ? */ + if ((void *)byte >= max) + { + result = false; + break; + } - *value |= ((byte & 0x7f) << shift); + *value |= ((*byte & 0x7f) << shift); + + result = true; shift += 7; } - while ((byte & 0x80) == 0x80); + while ((*byte++ & 0x80) == 0x80); /** * Le bit de signe n'est pas le bit de poids fort ici : * On travaille sur 7 bits, donc le masque est 0x40 ! */ - if (shift < LEB128_BITS_COUNT && (byte & 0x40) == 0x40) - *value |= (~0llu << shift); + if (result) + { + if (shift < LEB128_BITS_COUNT && (byte[-1] & 0x40) == 0x40) + *value |= (~0llu << shift); + } + + *pos = byte; return result; diff --git a/src/common/leb128.h b/src/common/leb128.h index f438068..cb712a3 100644 --- a/src/common/leb128.h +++ b/src/common/leb128.h @@ -30,7 +30,6 @@ #include "datatypes.h" -#include "packed.h" @@ -58,17 +57,23 @@ bool read_uleb128(uleb128_t *, const bin_t *, phys_t *, phys_t); /* Lit un nombre signé encodé au format LEB128. */ bool read_leb128(leb128_t *, const bin_t *, phys_t *, phys_t); +/* Charge un nombre non signé encodé au format LEB128. */ +bool load_uleb128(uleb128_t *, int); + +/* Sauvegarde un nombre non signé encodé au format LEB128. */ +bool store_uleb128(const uleb128_t *, int); + /* Encode un nombre non signé encodé au format LEB128. */ -bool pack_uleb128(const uleb128_t *, packed_buffer_t *); +void *pack_uleb128(const uleb128_t *, size_t *); /* Encode un nombre signé encodé au format LEB128. */ -bool pack_leb128(const leb128_t *, packed_buffer_t *); +void *pack_leb128(const leb128_t *, size_t *); -/* Décode un nombre non signé encodé au format LEB128. */ -bool unpack_uleb128(uleb128_t *, packed_buffer_t *); +/* Encode un nombre non signé encodé au format LEB128. */ +bool unpack_uleb128(uleb128_t *, const void **, const void *); /* Décode un nombre signé encodé au format LEB128. */ -bool unpack_leb128(leb128_t *, packed_buffer_t *); +bool unpack_leb128(leb128_t *, const void **, const void *); diff --git a/src/common/pathname.c b/src/common/pathname.c index dd3ec9e..81dc1e3 100644 --- a/src/common/pathname.c +++ b/src/common/pathname.c @@ -33,13 +33,13 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <gio/gio.h> #include <sys/stat.h> #include "extstr.h" #include "io.h" #include "../core/logs.h" -//#include "../core/params.h" // TODO : config @@ -316,16 +316,19 @@ int ensure_path_exists(const char *path) * Remarques : - * * * ******************************************************************************/ -#if 0 // TODO + int make_tmp_file(const char *prefix, const char *suffix, char **filename) { int result; /* Flux ou code à retourner */ - const char *tmpdir; /* Répertoire d'accueil */ - bool status; /* Bilan d'un consultation */ + GSettings *settings; /* Configuration sollicitée */ + gchar *tmpdir; /* Répertoire d'accueil */ size_t slen; /* Taille du suffixe */ - status = g_generic_config_get_value(get_main_configuration(), MPK_TMPDIR, &tmpdir); - if (!status) return -1; + /* Récupération d'un répertoire de travail temporaire */ + + settings = g_settings_new("re.chrysalide.framework.paths"); + + tmpdir = g_settings_get_string(settings, "tmp-work-dir"); slen = strlen(suffix); @@ -334,6 +337,12 @@ int make_tmp_file(const char *prefix, const char *suffix, char **filename) else asprintf(filename, "%s" G_DIR_SEPARATOR_S "%s-%d.XXXXXX", tmpdir, prefix, getpid()); + g_free(tmpdir); + + g_clear_object(&settings); + + /* Mise en place d'un fichier temporaire */ + result = ensure_path_exists(*filename); if (result == 0) @@ -356,7 +365,7 @@ int make_tmp_file(const char *prefix, const char *suffix, char **filename) return result; } -#endif + /****************************************************************************** * * diff --git a/src/common/pathname.h b/src/common/pathname.h index 1b6624c..104833b 100644 --- a/src/common/pathname.h +++ b/src/common/pathname.h @@ -42,9 +42,7 @@ bool mkpath(const char *); int ensure_path_exists(const char *); /* Met en place un fichier temporaire. */ -#if 0 // TODO int make_tmp_file(const char *, const char *, char **); -#endif /* Copie un fichier. */ bool copy_file(const char *, const char *); diff --git a/src/common/sort.h b/src/common/sort.h index 39a6f33..27e3739 100644 --- a/src/common/sort.h +++ b/src/common/sort.h @@ -37,6 +37,9 @@ int sort_boolean(bool, bool); /* Compare une valeur avec une autre. */ int sort_unsigned_long(unsigned long, unsigned long); +#define sort_size(v1, v2) \ + sort_unsigned_long(v1, v2) + /* Compare une valeur avec une autre. */ int sort_signed_long_long(signed long long, signed long long); diff --git a/src/common/szbin.h b/src/common/szbin.h new file mode 100644 index 0000000..8524ae3 --- /dev/null +++ b/src/common/szbin.h @@ -0,0 +1,282 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * szbin.h - prototypes pour une manipulation de données accompagnées d'une taille + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _COMMON_SZBIN_H +#define _COMMON_SZBIN_H + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include "datatypes.h" +#include "io.h" +#include "leb128.h" +#include "sort.h" + + + +/* Structure associant données et taille */ +typedef struct _sized_binary_t +{ + union { + + const char *static_data; /* Données non modifiées */ + char *data; /* Chaîne de caractères */ + + const bin_t *static_bin_data; /* Données brutes non modifiées*/ + bin_t *bin_data; /* Données brutes */ + + }; + + size_t size; /* Taille correspondante */ + +} sized_binary_t; + + +#define init_sized_binary(sb) \ + do \ + { \ + (sb)->data = NULL; \ + (sb)->size = 0; \ + } \ + while (0) + + +#define setup_sized_binary(sb, s) \ + do \ + { \ + (sb)->data = malloc(s); \ + (sb)->size = s; \ + } \ + while (0) + + +#define setup_sized_binary_from_static_string(sb, s) \ + do \ + { \ + (sb)->static_data = s; \ + (sb)->size = strlen(s) + 1; \ + } \ + while (0) + + +#define dup_into_sized_binary(sb, d, s) \ + do \ + { \ + size_t __size_once; \ + __size_once = s; \ + setup_sized_binary(sb, __size_once);\ + memcpy((sb)->data, d, __size_once); \ + } \ + while (0) + + +#define dup_sized_binary(dst, src) \ + dup_into_sized_binary((dst), (src)->static_data, (src)->size) + + +#define exit_sized_binary(sb) \ + do \ + { \ + if ((sb)->data != NULL) \ + { \ + free((sb)->data); \ + init_sized_binary(sb); \ + } \ + } \ + while (0) + + +#define resize_sized_binary(sb, s) \ + do \ + { \ + (sb)->size = s; \ + (sb)->data = realloc((sb)->data, \ + (sb)->size); \ + } \ + while (0) + + +#define add_to_sized_binary(sb, d, s) \ + do \ + { \ + size_t __old_size; \ + __old_size = (sb)->size; \ + (sb)->size += s; \ + (sb)->data = realloc((sb)->data, \ + (sb)->size); \ + memcpy((sb)->data + __old_size, \ + d, s); \ + } \ + while (0) + + +#define add_static_to_sized_binary(sb, d) \ + do \ + { \ + size_t __len; \ + __len = sizeof(d) - 1; \ + add_to_sized_binary(sb, d, __len); \ + } \ + while (0) + + +#define memcmp_sized_binary(s1, s2) \ + ({ \ + int __ret; \ + __ret = sort_size((s1)->size, (s2)->size); \ + if (__ret == 0) \ + __ret = memcmp((s1)->data, (s2)->data, (s1)->size); \ + __ret; \ + }) + + + +/** + * Conservations et rechargements. + */ + +#define load_sized_binary(sb, f) \ + ({ \ + uleb128_t __sz; \ + bool __ret; \ + __ret = load_uleb128(&__sz, f); \ + if (__ret) \ + { \ + setup_sized_binary(sb, __sz); \ + __ret = safe_read(f, (sb)->data, (sb)->size); \ + if (!__ret) \ + exit_sized_binary(sb); \ + } \ + __ret; \ + }) + + +#define load_sized_binary_as_string(sb, f) \ + ({ \ + uleb128_t __sz; \ + bool __ret; \ + __ret = load_uleb128(&__sz, f); \ + if (__ret) \ + { \ + setup_sized_binary(sb, __sz + 1); \ + __ret = safe_read(f, (sb)->data, __sz); \ + if (!__ret) \ + exit_sized_binary(sb); \ + (sb)->data[__sz] = '\0'; \ + } \ + __ret; \ + }) + + +#define store_sized_binary(sb, f) \ + ({ \ + bool __ret; \ + __ret = store_uleb128((const uleb128_t []){ (sb)->size }, f); \ + if (__ret) \ + __ret = safe_write(f, (sb)->static_data, (sb)->size); \ + __ret; \ + }) + + +#define store_sized_binary_as_string(sb, f) \ + ({ \ + bool __ret; \ + size_t __length; \ + assert((sb)->size >= 1); \ + __length = (sb)->size - 1; \ + assert((sb)->static_data[__length] == '\0'); \ + __ret = store_uleb128((const uleb128_t []){ __length }, f); \ + if (__ret) \ + __ret = safe_write(f, (sb)->static_data, __length); \ + __ret; \ + }) + + +#define unpack_sized_binary(sb, p, m) \ + ({ \ + uleb128_t __sz; \ + bool __ret; \ + __ret = unpack_uleb128(&__sz, p, m); \ + if (__ret) \ + { \ + setup_sized_binary(sb, __sz); \ + memcpy((sb)->data, *p, (sb)->size); \ + *((uint8_t **)p) += __sz; \ + } \ + __ret; \ + }) + + +#define unpack_sized_binary_as_string(sb, p, m) \ + ({ \ + uleb128_t __sz; \ + bool __ret; \ + __ret = unpack_uleb128(&__sz, p, m); \ + if (__ret) \ + { \ + setup_sized_binary(sb, __sz + 1); \ + memcpy((sb)->data, *p, __sz); \ + (sb)->data[__sz] = '\0'; \ + *((uint8_t **)p) += __sz; \ + } \ + __ret; \ + }) + + +#define pack_sized_binary(sb, l) \ + ({ \ + uint8_t *__result; \ + size_t __pos; \ + __result = pack_uleb128((const uleb128_t []){ (sb)->size }, l); \ + __pos = *(l); \ + *(l) += (sb)->size; \ + __result = realloc(__result, *(l) * sizeof(uint8_t)); \ + memcpy(&__result[__pos], (sb)->static_data, \ + ((sb)->size * sizeof(uint8_t))); \ + __result; \ + }) + + +#define pack_sized_binary_as_string(sb, l) \ + ({ \ + uint8_t *__result; \ + size_t __length; \ + size_t __pos; \ + assert((sb)->size >= 1); \ + __length = (sb)->size - 1; \ + assert((sb)->static_data[__length] == '\0'); \ + __result = pack_uleb128((const uleb128_t []){ __length }, l); \ + __pos = *(l); \ + *(l) += __length; \ + __result = realloc(__result, *(l) * sizeof(uint8_t)); \ + memcpy(&__result[__pos], (sb)->static_data, \ + (__length * sizeof(uint8_t))); \ + __result; \ + }) + + + +#endif /* _COMMON_SZBIN_H */ diff --git a/src/common/szstr.h b/src/common/szstr.h index 406a9f1..c48d81f 100644 --- a/src/common/szstr.h +++ b/src/common/szstr.h @@ -54,7 +54,7 @@ typedef struct _sized_string_t typedef sized_string_t sized_binary_t; - +/* #define init_szstr(s) \ do \ { \ @@ -62,6 +62,7 @@ typedef sized_string_t sized_binary_t; (s)->len = 0; \ } \ while (0) +*/ #define szstrdup(dst, src) \ do \ @@ -74,6 +75,7 @@ typedef sized_string_t sized_binary_t; #define copy_szstr(d, s) (d) = (s); +/* #define exit_szstr(s) \ do \ { \ @@ -84,6 +86,7 @@ typedef sized_string_t sized_binary_t; } \ } \ while (0) +*/ #define szstrcmp(s1, s2) \ ({ \ diff --git a/src/common/xdg.c b/src/common/xdg.c index cabff75..eb64dad 100644 --- a/src/common/xdg.c +++ b/src/common/xdg.c @@ -24,20 +24,41 @@ #include "xdg.h" -#include <dirent.h> -#include <errno.h> +#include <assert.h> #include <glib.h> #include <malloc.h> -#include <stdlib.h> #include <string.h> +#include <unistd.h> + + +#include "pathname.h" + + + +/** + * Cf. https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ + +/* $HOME/.cache */ +#define CACHE_HOME_SUFFIX ".cache" G_DIR_SEPARATOR_S + +/* $HOME/.config */ +#define CONFIG_HOME_SUFFIX ".config" G_DIR_SEPARATOR_S + +/* $HOME/.local/share */ +#define DATA_HOME_SUFFIX ".local" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S + +/* $HOME/.local/state */ +#define STATE_HOME_SUFFIX ".local" G_DIR_SEPARATOR_S "state" G_DIR_SEPARATOR_S /****************************************************************************** * * * Paramètres : suffix = élément visé dans le répertoire de configuration. * +* create = assure la mise en place du répertoire final. * * * -* Description : Détermine le chemin d'un répertoire selon les specs. XDG. * +* Description : Détermine le chemin d'un répertoire de données XDG. * * * * Retour : Chemin d'accès aux configurations personnelles ou NULL. * * * @@ -45,70 +66,321 @@ * * ******************************************************************************/ -char *get_xdg_config_dir(const char *suffix) +char *get_xdg_cache_dir(const char *suffix, bool create) { char *result; /* Chemin d'accès à renvoyer */ const char *env; /* Valeur de l'environnement */ - DIR *directory; /* Répertoire avec contenu ? */ - struct dirent *entry; /* Elément de répertoire */ + int ret; /* Bilan d'une assurance */ + + assert(suffix[0] != G_DIR_SEPARATOR); result = NULL; - env = getenv("XDG_CONFIG_HOME"); + env = getenv("XDG_CACHE_HOME"); if (env != NULL && env[0] != '\0') { - directory = opendir(env); - if (directory == NULL) goto default_cfg_dir; + result = calloc(strlen(env) + 1 + strlen(suffix) + 1, sizeof(char)); + + strcpy(result, env); + + if (env[strlen(env) - 1] != G_DIR_SEPARATOR) + strcat(result, G_DIR_SEPARATOR_S); + + strcat(result, suffix); + + } + + else + { + env = getenv("HOME"); + if (env == NULL || env[0] == '\0') goto no_env; + + result = calloc(strlen(env) + 1 + strlen(CACHE_HOME_SUFFIX) + strlen(suffix) + 1, sizeof(char)); + + strcpy(result, env); + + if (env[strlen(env) - 1] != G_DIR_SEPARATOR) + strcat(result, G_DIR_SEPARATOR_S); + + strcat(result, CACHE_HOME_SUFFIX); + strcat(result, suffix); + + } + + if (create) + { + ret = ensure_path_exists(result); - while (1) + if (ret != 0) { - errno = 0; + free(result); + result = NULL; + } - entry = readdir(directory); + } - if (entry == NULL) - { - if (errno != 0) - perror("readdir"); + no_env: - break; + return result; + +} - } - if (strcmp(entry->d_name, ".") == 0) continue; - if (strcmp(entry->d_name, "..") == 0) continue; +/****************************************************************************** +* * +* Paramètres : suffix = élément visé dans le répertoire de configuration. * +* create = assure la mise en place du répertoire final. * +* * +* Description : Détermine le chemin d'un répertoire de données XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou NULL. * +* * +* Remarques : cf. http://standards.freedesktop.org/basedir-spec/. * +* * +******************************************************************************/ - result = calloc(strlen(env) + 2 + strlen(suffix) + 1, sizeof(char)); - strcpy(result, env); +char *get_xdg_config_dir(const char *suffix, bool create) +{ + char *result; /* Chemin d'accès à renvoyer */ + const char *env; /* Valeur de l'environnement */ + int ret; /* Bilan d'une assurance */ - if (env[strlen(env) - 1] != G_DIR_SEPARATOR) - strcat(result, G_DIR_SEPARATOR_S); + assert(suffix[0] != G_DIR_SEPARATOR); - strcat(result, "."); - strcat(result, suffix); + result = NULL; + env = getenv("XDG_CONFIG_HOME"); + + if (env != NULL && env[0] != '\0') + { + result = calloc(strlen(env) + 1 + strlen(suffix) + 1, sizeof(char)); + + strcpy(result, env); + + if (env[strlen(env) - 1] != G_DIR_SEPARATOR) + strcat(result, G_DIR_SEPARATOR_S); + + strcat(result, suffix); + + } + + else + { + env = getenv("HOME"); + if (env == NULL || env[0] == '\0') goto no_env; + + result = calloc(strlen(env) + 1 + strlen(CONFIG_HOME_SUFFIX) + strlen(suffix) + 1, sizeof(char)); + + strcpy(result, env); + + if (env[strlen(env) - 1] != G_DIR_SEPARATOR) + strcat(result, G_DIR_SEPARATOR_S); + + strcat(result, CONFIG_HOME_SUFFIX); + strcat(result, suffix); + + } + + if (create) + { + ret = ensure_path_exists(result); + + if (ret != 0) + { + free(result); + result = NULL; } - closedir(directory); + } + + no_env: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : suffix = élément visé dans le répertoire de configuration. * +* create = assure la mise en place du répertoire final. * +* * +* Description : Détermine le chemin d'un répertoire de données XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou NULL. * +* * +* Remarques : cf. http://standards.freedesktop.org/basedir-spec/. * +* * +******************************************************************************/ + +char *get_xdg_data_dir(const char *suffix, bool create) +{ + char *result; /* Chemin d'accès à renvoyer */ + const char *env; /* Valeur de l'environnement */ + int ret; /* Bilan d'une assurance */ + + assert(suffix[0] != G_DIR_SEPARATOR); + + result = NULL; + + env = getenv("XDG_DATA_HOME"); + + if (env != NULL && env[0] != '\0') + { + result = calloc(strlen(env) + 1 + strlen(suffix) + 1, sizeof(char)); + + strcpy(result, env); + + if (env[strlen(env) - 1] != G_DIR_SEPARATOR) + strcat(result, G_DIR_SEPARATOR_S); + + strcat(result, suffix); + + } + + else + { + env = getenv("HOME"); + if (env == NULL || env[0] == '\0') goto no_env; + + result = calloc(strlen(env) + 1 + strlen(DATA_HOME_SUFFIX) + strlen(suffix) + 1, sizeof(char)); + + strcpy(result, env); + + if (env[strlen(env) - 1] != G_DIR_SEPARATOR) + strcat(result, G_DIR_SEPARATOR_S); + + strcat(result, DATA_HOME_SUFFIX); + strcat(result, suffix); } - default_cfg_dir: + if (create) + { + ret = ensure_path_exists(result); + + if (ret != 0) + { + free(result); + result = NULL; + } + + } + + no_env: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : suffix = élément visé dans le répertoire de configuration. * +* create = assure la mise en place du répertoire final. * +* * +* Description : Détermine le chemin d'un répertoire de données XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou NULL. * +* * +* Remarques : cf. http://standards.freedesktop.org/basedir-spec/. * +* * +******************************************************************************/ + +char *get_xdg_state_dir(const char *suffix, bool create) +{ + char *result; /* Chemin d'accès à renvoyer */ + const char *env; /* Valeur de l'environnement */ + int ret; /* Bilan d'une assurance */ + + assert(suffix[0] != G_DIR_SEPARATOR); + + result = NULL; + + env = getenv("XDG_STATE_HOME"); + + if (env != NULL && env[0] != '\0') + { + result = calloc(strlen(env) + 1 + strlen(suffix) + 1, sizeof(char)); + + strcpy(result, env); + + if (env[strlen(env) - 1] != G_DIR_SEPARATOR) + strcat(result, G_DIR_SEPARATOR_S); - if (result == NULL) + strcat(result, suffix); + + } + + else { env = getenv("HOME"); - if (env == NULL || env[0] == '\0') return NULL; + if (env == NULL || env[0] == '\0') goto no_env; + + result = calloc(strlen(env) + 1 + strlen(STATE_HOME_SUFFIX) + strlen(suffix) + 1, sizeof(char)); + + strcpy(result, env); + + if (env[strlen(env) - 1] != G_DIR_SEPARATOR) + strcat(result, G_DIR_SEPARATOR_S); + + strcat(result, STATE_HOME_SUFFIX); + strcat(result, suffix); + + } + + if (create) + { + ret = ensure_path_exists(result); + + if (ret != 0) + { + free(result); + result = NULL; + } + + } - result = calloc(strlen(env) + 1 + strlen(".config" G_DIR_SEPARATOR_S) + strlen(suffix) + 1, sizeof(char)); + no_env: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : suffix = élément visé dans le répertoire de configuration. * +* * +* Description : Détermine le chemin d'un répertoire éphémère XDG. * +* * +* Retour : Chemin d'accès aux configurations personnelles ou NULL. * +* * +* Remarques : cf. http://standards.freedesktop.org/basedir-spec/. * +* * +******************************************************************************/ + +char *get_xdg_runtime_dir(const char *suffix) +{ + char *result; /* Chemin d'accès à renvoyer */ + const char *env; /* Valeur de l'environnement */ + + assert(suffix[0] != G_DIR_SEPARATOR); + + result = NULL; + + env = getenv("XDG_RUNTIME_DIR"); + + if (env != NULL && env[0] != '\0') + { + result = calloc(strlen(env) + 1 + strlen(suffix) + 1, sizeof(char)); strcpy(result, env); if (env[strlen(env) - 1] != G_DIR_SEPARATOR) strcat(result, G_DIR_SEPARATOR_S); - strcat(result, ".config" G_DIR_SEPARATOR_S); strcat(result, suffix); } diff --git a/src/common/xdg.h b/src/common/xdg.h index c9c2327..a6cd91d 100644 --- a/src/common/xdg.h +++ b/src/common/xdg.h @@ -25,9 +25,24 @@ #define _COMMON_XDG_H +#include <stdbool.h> -/* Détermine le chemin d'un répertoire selon les specs. XDG. */ -char *get_xdg_config_dir(const char *); + + +/* Détermine le chemin d'un répertoire de données XDG. */ +char *get_xdg_cache_dir(const char *, bool); + +/* Détermine le chemin d'un répertoire de données XDG. */ +char *get_xdg_config_dir(const char *, bool); + +/* Détermine le chemin d'un répertoire de données XDG. */ +char *get_xdg_data_dir(const char *, bool); + +/* Détermine le chemin d'un répertoire de données XDG. */ +char *get_xdg_state_dir(const char *, bool); + +/* Détermine le chemin d'un répertoire éphémère XDG. */ +char *get_xdg_runtime_dir(const char *); diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 5bcb13a..15ed866 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -4,11 +4,8 @@ noinst_LTLIBRARIES = libcore4.la # libcore.la libcore_la_SOURCES = \ collections.h collections.c \ columns.h \ - core.h core.c \ demanglers.h demanglers.c \ - global.h global.c \ logs.h logs.c \ - nproc.h nproc.c \ params.h params.c \ paths.h paths.c \ processors.h processors.c \ @@ -18,10 +15,15 @@ libcore_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) libcore4_la_SOURCES = \ + core.h core.c \ + global.h global.c \ logs.h logs.c \ - paths.h paths.c + nox.h nox.c \ + nproc.h nproc.c \ + paths.h paths.c \ + processors.h processors.c -libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS) +libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS) devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) diff --git a/src/core/core.c b/src/core/core.c index f67e23d..c730fad 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.c - chargement et le déchargement du tronc commun * - * Copyright (C) 2014-2019 Cyrille Bagard + * Copyright (C) 2014-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -24,6 +24,97 @@ #include "core.h" +#include "global.h" +#include "processors.h" + + + +/* Statuts de chargement */ +static AvailableCoreComponent __loaded = ACC_NONE; + + + +/****************************************************************************** +* * +* Paramètres : flags = liste d'éléments à charger. * +* * +* Description : Charge une sélection d'éléments de base du programme. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool load_core_components(AvailableCoreComponent flags) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if ((flags & ACC_GLOBAL_VARS) != 0 && (__loaded & ACC_GLOBAL_VARS) == 0) + { + set_secret_storage(g_secret_storage_new(NULL)); + + set_work_queue(g_work_queue_new()); + + __loaded |= ACC_GLOBAL_VARS; + + } + + if ((flags & ACC_CODE_ANALYSIS) != 0 && (__loaded & ACC_CODE_ANALYSIS) == 0) + { + register_arch_gtypes(); + + init_operands_factory(); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : flags = liste d'éléments à décharger. * +* * +* Description : Décharge une sélection d'éléments de base du programme. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void unload_core_components(AvailableCoreComponent flags) +{ + if ((flags & ACC_CODE_ANALYSIS) != 0 && (__loaded & ACC_CODE_ANALYSIS) == 0) + { + exit_operands_factory(); + + } + + if ((flags & ACC_GLOBAL_VARS) != 0 && (__loaded & ACC_GLOBAL_VARS) == 0) + { + set_work_queue(NULL); + + set_secret_storage(NULL); + + __loaded &= ~ACC_GLOBAL_VARS; + + } + +} + + + + + + + +#if 0 + #include <stdlib.h> #include <time.h> #include <unistd.h> @@ -35,7 +126,6 @@ #include "demanglers.h" #include "global.h" #include "params.h" -#include "processors.h" #include "queue.h" #include "../analysis/scan/core.h" #ifdef INCLUDE_MAGIC_SUPPORT @@ -119,9 +209,6 @@ bool load_all_core_components(bool cs) if (result) result = init_segment_content_hash_table(); - register_arch_gtypes(); - init_operands_factory(); - } } @@ -147,8 +234,6 @@ void unload_all_core_components(bool cs) { if (cs) { - exit_operands_factory(); - exit_segment_content_hash_table(); unload_demanglers_definitions(); @@ -255,3 +340,5 @@ void unload_core_components(AvailableCoreComponent flags) } } + +#endif diff --git a/src/core/core.h b/src/core/core.h index def2813..640476a 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.h - prototypes pour le chargement et le déchargement du tronc commun * - * Copyright (C) 2014-2018 Cyrille Bagard + * Copyright (C) 2014-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -29,17 +29,15 @@ -/* Charge les éléments de base du programme. */ -bool load_all_core_components(bool); - -/* Décharge les éléments de base du programme. */ -void unload_all_core_components(bool); - - /* Eléments à (dé)charger disponibles */ typedef enum _AvailableCoreComponent { - ACC_SCAN_FEATURES = (1 << 0), /* Espace de noms pour scan */ + ACC_NONE = (0 << 0), /* Statut initial */ + ACC_GLOBAL_VARS = (1 << 0), /* Singletons globaux */ + ACC_CODE_ANALYSIS = (1 << 1), /* Désassemblage de code */ + ACC_SCAN_FEATURES = (1 << 2), /* Espace de noms pour scan */ + + ACC_ALL_COMPONENTS = (1 << 3) - 1 } AvailableCoreComponent; diff --git a/src/core/global.c b/src/core/global.c index c99d711..0275e09 100644 --- a/src/core/global.c +++ b/src/core/global.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * global.c - conservation et accès aux variables globales * - * Copyright (C) 2017-2019 Cyrille Bagard + * Copyright (C) 2017-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -24,38 +24,29 @@ #include "global.h" -#include <assert.h> +#include "../glibext/helpers.h" -/* Mode de fonctionnement */ -static bool _batch_mode = false; +/* Décompte des émissions et réceptions */ +static GMutex _network_mutex; +static size_t _bytes_received = 0; +static size_t _bytes_sent = 0; /* Gestionnaire de tâches parallèles */ static GWorkQueue *_queue = NULL; -/* Explorateur de contenus */ -static GContentExplorer *_explorer = NULL; - -/* Résolveur de contenus */ -static GContentResolver *_resolver = NULL; - -/* Espace de noms racine pour ROST */ -static GScanNamespace *_rost_root_ns = NULL; - -/* Projet global actif */ -static GStudyProject *_project = NULL; - -/* Avertisseur de changement de projet principal */ -static current_project_change_cb _project_notify = NULL; +/* Gardien des secrets avec support des stockages */ +static GSecretStorage *_storage = NULL; /****************************************************************************** * * -* Paramètres : - * +* Paramètres : received = quantité d'octets reçus jusqu'à présent. [OUT] * +* sent = quantité d'octets émis jusqu'à présent. [OUT] * * * -* Description : Note un mode d'exécution sans interface. * +* Description : Fournit les volumes d'octets échangés sur le réseau. * * * * Retour : - * * * @@ -63,28 +54,39 @@ static current_project_change_cb _project_notify = NULL; * * ******************************************************************************/ -void set_batch_mode(void) +void get_network_stats(size_t *received, size_t *sent) { - _batch_mode = true; + g_mutex_lock(&_network_mutex); + + *received = _bytes_received; + *sent = _bytes_sent; + + g_mutex_unlock(&_network_mutex); } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : received = quantité d'octets reçus supplémentaire. * +* sent = quantité d'octets émis supplémentaire. * * * -* Description : Indique le mode d'exécution courant du programme. * +* Description : Augmente les volumes d'octets échangés sur le réseau. * * * -* Retour : true si le fonctionnement est sans interface. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -bool is_batch_mode(void) +void update_network_stats(size_t received, size_t sent) { - return _batch_mode; + g_mutex_lock(&_network_mutex); + + _bytes_received += received; + _bytes_sent += sent; + + g_mutex_unlock(&_network_mutex); } @@ -101,9 +103,10 @@ bool is_batch_mode(void) * * ******************************************************************************/ -void set_work_queue(GWorkQueue *queue) +void set_work_queue(/* __steal */GWorkQueue *queue) { - assert(_queue == NULL); + if (_queue != NULL) + unref_object(_queue); _queue = queue; @@ -124,6 +127,8 @@ void set_work_queue(GWorkQueue *queue) GWorkQueue *get_work_queue(void) { + ref_object(_queue); + return _queue; } @@ -131,6 +136,131 @@ GWorkQueue *get_work_queue(void) /****************************************************************************** * * +* Paramètres : queue = nouveau stockage sécurisé à mémoriser ou NULL. * +* * +* Description : Définit le stockage sécurisé principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void set_secret_storage(/* __steal */GSecretStorage *storage) +{ + if (_storage != NULL) + unref_object(_storage); + + _storage = storage; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit le stockage sécurisé principal. * +* * +* Retour : Gestionnaire de traitements parallèles courant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSecretStorage *get_secret_storage(void) +{ + ref_object(_storage); + + return _storage; + +} + + + + + + + + + + + + + + + +#if 0 + +#include <assert.h> + + + +/* Mode de fonctionnement */ +static bool _batch_mode = false; + +/* Gestionnaire de tâches parallèles */ +//static GWorkQueue *_queue = NULL; + +/* Explorateur de contenus */ +static GContentExplorer *_explorer = NULL; + +/* Résolveur de contenus */ +static GContentResolver *_resolver = NULL; + +/* Espace de noms racine pour ROST */ +static GScanNamespace *_rost_root_ns = NULL; + +/* Projet global actif */ +static GStudyProject *_project = NULL; + +/* Avertisseur de changement de projet principal */ +static current_project_change_cb _project_notify = NULL; + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Note un mode d'exécution sans interface. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void set_batch_mode(void) +{ + _batch_mode = true; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Indique le mode d'exécution courant du programme. * +* * +* Retour : true si le fonctionnement est sans interface. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool is_batch_mode(void) +{ + return _batch_mode; + +} + + + + +/****************************************************************************** +* * * Paramètres : explorer = éventuelle adresse du nouveau gestionnaire. * * * * Description : Définit l'adresse de l'explorateur de contenus courant. * @@ -343,3 +473,5 @@ void register_project_change_notification(current_project_change_cb notify) _project_notify = notify; } + +#endif diff --git a/src/core/global.h b/src/core/global.h index 0a9172b..f5d8a62 100644 --- a/src/core/global.h +++ b/src/core/global.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * global.h - prototypes pour la conservation et l'accès aux variables globales * - * Copyright (C) 2017-2018 Cyrille Bagard + * Copyright (C) 2017-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,6 +25,35 @@ #define _CORE_GLOBAL_H +#include "../glibext/secstorage.h" +#include "../glibext/workqueue.h" + + + +/* Fournit les volumes d'octets échangés sur le réseau. */ +void get_network_stats(size_t *, size_t *); + +/* Augmente les volumes d'octets échangés sur le réseau. */ +void update_network_stats(size_t, size_t); + +/* Définit le gestionnaire de traitements parallèles courant. */ +void set_work_queue(/* __steal */GWorkQueue *); + +/* Fournit le gestionnaire de traitements parallèles courant. */ +GWorkQueue *get_work_queue(void); + +/* Définit le stockage sécurisé principal. */ +void set_secret_storage(/* __steal */GSecretStorage *); + +/* Fournit le stockage sécurisé principal. */ +GSecretStorage *get_secret_storage(void); + + + + + +#if 0 + #include <stdbool.h> @@ -42,10 +71,10 @@ void set_batch_mode(void); bool is_batch_mode(void); /* Définit le gestionnaire de traitements parallèles courant. */ -void set_work_queue(GWorkQueue *); +//void set_work_queue(GWorkQueue *); /* Fournit le gestionnaire de traitements parallèles courant. */ -GWorkQueue *get_work_queue(void); +//GWorkQueue *get_work_queue(void); /* Définit l'adresse de l'explorateur de contenus courant. */ void set_current_content_explorer(GContentExplorer *); @@ -78,5 +107,8 @@ typedef void (* current_project_change_cb) (GStudyProject *, bool); void register_project_change_notification(current_project_change_cb); +#endif + + #endif /* _CORE_GLOBAL_H */ diff --git a/src/core/logs.h b/src/core/logs.h index e8df8bd..3719c6b 100644 --- a/src/core/logs.h +++ b/src/core/logs.h @@ -162,6 +162,15 @@ void log_variadic_message(LogMessageType, const char *, ...); } \ while (0) +#define LOG_ERROR_ZIP(func, err) \ + do \ + { \ + const char *__msg; \ + __msg = zip_error_strerror(err); \ + log_variadic_message(LMT_EXT_ERROR, "[%s:%u] %s: %s", __FUNCTION__, __LINE__, func, __msg); \ + } \ + while (0) + #endif /* _CORE_LOGS_H */ diff --git a/plugins/pe/section.c b/src/core/nox.c index 732b35c..ccf9de2 100644 --- a/plugins/pe/section.c +++ b/src/core/nox.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * section.h - prototypes pour la gestion des sections d'un PE + * nox.c - indication de présence ou d'absence de support graphique * - * Copyright (C) 2010-2021 Cyrille Bagard + * Copyright (C) 2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,52 +21,41 @@ */ -#include "section.h" +#include "nox.h" -#include <malloc.h> +#include <stddef.h> -#include "pe-int.h" +#include "../common/compiler.h" + + + +/* Indique la présence ou l'absence d'un affichage graphique. */ +__weak bool _run_in_nox_mode(void); /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* pos = tête de lecture positionnée. [OUT] * +* Paramètres : - * * * -* Description : Recherche une section donnée au sein de binaire par indice. * +* Description : Indique la présence ou l'absence d'un affichage graphique. * * * -* Retour : Liste de sections reconstituées ou NULL en cas d'échec. * +* Retour : Statut à transmettre. * * * * Remarques : - * * * ******************************************************************************/ -image_section_header *read_all_pe_sections(const GPeFormat *format, vmpa2t *pos) +bool run_in_nox_mode(void) { - image_section_header *result; /* Liste à retourner */ - uint16_t count; /* Quantité de sections */ - uint16_t i; /* Boucle de parcours */ - bool status; /* Bilan d'une lecture */ - - count = format->nt_headers.file_header.number_of_sections; - - result = malloc(count * sizeof(image_section_header)); - - for (i = 0; i < count; i++) - { - status = read_pe_image_section_header(format, pos, &result[i]); - - if (!status) - { - free(result); - result = NULL; - break; - } + bool result; /* Statut à retournr */ - } + if (_run_in_nox_mode == NULL) + result = true; + else + result = _run_in_nox_mode(); return result; diff --git a/src/core/nox.h b/src/core/nox.h new file mode 100644 index 0000000..c2c225d --- /dev/null +++ b/src/core/nox.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * nox.h - prototypes pour l'indication de présence ou d'absence de support graphique + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _CORE_NOX_H +#define _CORE_NOX_H + + +#include <stdbool.h> + + + +/* Indique la présence ou l'absence d'un affichage graphique. */ +bool run_in_nox_mode(void); + + + +#endif /* _CORE_NOX_H */ diff --git a/src/core/processors.c b/src/core/processors.c index e4a558f..056fc23 100644 --- a/src/core/processors.c +++ b/src/core/processors.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * processors.c - enregistrement et fourniture des architectures supportées * - * Copyright (C) 2015-2020 Cyrille Bagard + * Copyright (C) 2015-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -29,13 +29,13 @@ #include <pthread.h> #include <string.h> - +#if 0 #include "../arch/instructions/raw.h" #include "../arch/instructions/undefined.h" #include "../arch/operands/immediate.h" #include "../arch/operands/register.h" #include "../arch/operands/target.h" - +#endif /* Cache des singletons d'opérandes */ @@ -77,12 +77,12 @@ static proc_t *find_processor_by_key(const char *); void register_arch_gtypes(void) { - g_type_ensure(G_TYPE_RAW_INSTRUCTION); - g_type_ensure(G_TYPE_UNDEF_INSTRUCTION); + //g_type_ensure(G_TYPE_RAW_INSTRUCTION); + //g_type_ensure(G_TYPE_UNDEF_INSTRUCTION); - g_type_ensure(G_TYPE_IMM_OPERAND); - g_type_ensure(G_TYPE_REGISTER_OPERAND); - g_type_ensure(G_TYPE_TARGET_OPERAND); + //g_type_ensure(G_TYPE_IMM_OPERAND); + //g_type_ensure(G_TYPE_REGISTER_OPERAND); + //g_type_ensure(G_TYPE_TARGET_OPERAND); } @@ -126,7 +126,7 @@ GSingletonFactory *get_operands_factory(void) result = __operands_factory; - g_object_ref(G_OBJECT(result)); + ref_object(result); return result; @@ -154,7 +154,7 @@ void exit_operands_factory(void) } - +#if 0 /****************************************************************************** * * * Paramètres : type = type GLib représentant le type à instancier. * @@ -206,7 +206,7 @@ bool register_processor_type(GType type) done: - g_object_unref(G_OBJECT(proc)); + unref_object(proc); return result; @@ -341,3 +341,4 @@ GArchProcessor *get_arch_processor_for_key(const char *key) return result; } +#endif diff --git a/src/core/processors.h b/src/core/processors.h index 6aa2d1e..b622305 100644 --- a/src/core/processors.h +++ b/src/core/processors.h @@ -29,7 +29,7 @@ #include <stdbool.h> -#include "../arch/processor.h" +//#include "../arch/processor.h" #include "../glibext/singleton.h" @@ -45,7 +45,7 @@ GSingletonFactory *get_operands_factory(void); /* Supprime le fournisseur d'instances uniques d'opérandes. */ void exit_operands_factory(void); - +#if 0 /* Enregistre un processeur pour une architecture donnée. */ bool register_processor_type(GType); @@ -57,7 +57,7 @@ char **get_all_processor_keys(size_t *); /* Fournit le processeur d'architecture correspondant à un nom. */ GArchProcessor *get_arch_processor_for_key(const char *); - +#endif #endif /* _CORE_PROCESSORS_H */ diff --git a/src/data/Makefile.am b/src/data/Makefile.am new file mode 100644 index 0000000..8cbe67a --- /dev/null +++ b/src/data/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = images diff --git a/src/data/images/Makefile.am b/src/data/images/Makefile.am new file mode 100644 index 0000000..d5ec84f --- /dev/null +++ b/src/data/images/Makefile.am @@ -0,0 +1,33 @@ + +BUILT_SOURCES = resources.h resources.c + +noinst_LTLIBRARIES = libdataimages.la + +RES_FILES = \ + about-bg.png \ + dock-station-left-symbolic.svg \ + dock-station-right-symbolic.svg \ + dock-station-bottom-symbolic.svg \ + logs-symbolic.svg + +libdataimages_la_SOURCES = \ + resources.h resources.c + +libdataimages_la_CFLAGS = $(LIBGIOUNIX_CFLAGS) + + +devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) + +dev_HEADERS = $(libdataimages_la_SOURCES:%c=) + + +resources.c: gresource.xml $(RES_FILES) + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name data_images gresource.xml + +resources.h: gresource.xml + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name data_images gresource.xml + + +CLEANFILES = resources.h resources.c + +EXTRA_DIST = gresource.xml $(RES_FILES) diff --git a/src/data/images/dock-station-bottom-symbolic.svg b/src/data/images/dock-station-bottom-symbolic.svg new file mode 100644 index 0000000..37084ac --- /dev/null +++ b/src/data/images/dock-station-bottom-symbolic.svg @@ -0,0 +1,197 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="180" + height="180" + viewBox="0 0 180 180" + version="1.1" + id="svg5" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="dock-station-bottom-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview7" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="true" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="false" + inkscape:zoom="4.2708118" + inkscape:cx="87.219952" + inkscape:cy="89.444353" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="g3929" /> + <defs + id="defs2"> + <clipPath + id="k"> + <path + d="M 0,0 H 800 V 800 H 0 Z" + id="path1320" /> + </clipPath> + <mask + id="j"> + <g + filter="url(#a)" + id="g1317"> + <path + d="M 0,0 H 16 V 16 H 0 Z" + fill-opacity="0.35" + id="path1315" /> + </g> + </mask> + <filter + id="a" + height="1" + width="1" + x="0" + y="0"> + <feColorMatrix + in="SourceGraphic" + type="matrix" + values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0" + id="feColorMatrix1280" /> + </filter> + <clipPath + id="i"> + <path + d="M 0,0 H 800 V 800 H 0 Z" + id="path1312" /> + </clipPath> + <mask + id="h"> + <g + filter="url(#a)" + id="g1309"> + <path + d="M 0,0 H 16 V 16 H 0 Z" + fill-opacity="0.35" + id="path1307" /> + </g> + </mask> + <filter + id="filter3652" + height="1" + width="1" + x="0" + y="0"> + <feColorMatrix + in="SourceGraphic" + type="matrix" + values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0" + id="feColorMatrix3650" /> + </filter> + <clipPath + id="g"> + <path + d="M 0,0 H 800 V 800 H 0 Z" + id="path1304" /> + </clipPath> + <mask + id="f"> + <g + filter="url(#a)" + id="g1301"> + <path + d="M 0,0 H 16 V 16 H 0 Z" + fill-opacity="0.35" + id="path1299" /> + </g> + </mask> + <filter + id="filter3661" + height="1" + width="1" + x="0" + y="0"> + <feColorMatrix + in="SourceGraphic" + type="matrix" + values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0" + id="feColorMatrix3659" /> + </filter> + <clipPath + id="e"> + <path + d="M 0,0 H 800 V 800 H 0 Z" + id="path1296" /> + </clipPath> + <mask + id="d"> + <g + filter="url(#a)" + id="g1293"> + <path + d="M 0,0 H 16 V 16 H 0 Z" + fill-opacity="0.35" + id="path1291" /> + </g> + </mask> + <filter + id="filter3670" + height="1" + width="1" + x="0" + y="0"> + <feColorMatrix + in="SourceGraphic" + type="matrix" + values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0" + id="feColorMatrix3668" /> + </filter> + <clipPath + id="c"> + <path + d="M 0,0 H 800 V 800 H 0 Z" + id="path1288" /> + </clipPath> + <mask + id="b"> + <g + filter="url(#a)" + id="g1285"> + <path + d="M 0,0 H 16 V 16 H 0 Z" + fill-opacity="0.3" + id="path1283" /> + </g> + </mask> + <filter + id="filter3679" + height="1" + width="1" + x="0" + y="0"> + <feColorMatrix + in="SourceGraphic" + type="matrix" + values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0" + id="feColorMatrix3677" /> + </filter> + </defs> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="g3929"> + <path + id="rect3921" + style="fill:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke;fill-opacity:1" + d="M 30 15 C 13.380034 15 0 28.380034 0 45 L 0 100 L 0 110 L 0 135 C 0 151.61996 13.380034 165 30 165 L 150 165 C 166.61996 165 180 151.61996 180 135 L 180 110 L 180 100 L 180 45 C 180 28.380034 166.61996 15 150 15 L 30 15 z M 35 35 L 145 35 C 153.30998 35 160 41.690016 160 50 L 160 100 L 20 100 L 20 50 C 20 41.690016 26.690016 35 35 35 z M 20 110 L 160 110 L 160 130 C 160 138.30998 153.30998 145 145 145 L 35 145 C 26.690016 145 20 138.30998 20 130 L 20 110 z " /> + <path + id="rect3923" + style="fill:#000000;fill-opacity:0.41999999;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 20 110 L 20 130 C 20 138.30999 26.690008 145 35 145 L 145 145 C 153.30999 145 160 138.30999 160 130 L 160 110 L 20 110 z " /> + </g> +</svg> diff --git a/src/data/images/dock-station-left-symbolic.svg b/src/data/images/dock-station-left-symbolic.svg new file mode 100644 index 0000000..0f90e8f --- /dev/null +++ b/src/data/images/dock-station-left-symbolic.svg @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="180" + height="180" + viewBox="0 0 180 180" + version="1.1" + id="svg5" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="dock-station-left-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview7" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="true" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="false" + inkscape:zoom="4.4555556" + inkscape:cx="88.541146" + inkscape:cy="89.999999" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" /> + <defs + id="defs2" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + id="rect254" + style="fill:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke;fill-opacity:1" + d="M 30 15 C 13.380034 15 0 28.380034 0 45 L 0 135 C 0 151.61996 13.380034 165 30 165 L 65 165 L 75 165 L 150 165 C 166.61996 165 180 151.61996 180 135 L 180 45 C 180 28.380034 166.61996 15 150 15 L 75 15 L 65 15 L 30 15 z M 35 35 L 65 35 L 65 145 L 35 145 C 26.690016 145 20 138.30998 20 130 L 20 50 C 20 41.690016 26.690016 35 35 35 z M 75 35 L 145 35 C 153.30998 35 160 41.690016 160 50 L 160 130 C 160 138.30998 153.30998 145 145 145 L 75 145 L 75 35 z " /> + <path + id="rect390" + style="fill:#000000;fill-opacity:0.41999999;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 35 35 C 26.690008 35 20 41.690008 20 50 L 20 130 C 20 138.30999 26.690008 145 35 145 L 65 145 L 65 35 L 35 35 z " /> + </g> +</svg> diff --git a/src/data/images/dock-station-right-symbolic.svg b/src/data/images/dock-station-right-symbolic.svg new file mode 100644 index 0000000..774476a --- /dev/null +++ b/src/data/images/dock-station-right-symbolic.svg @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="180" + height="180" + viewBox="0 0 180 180" + version="1.1" + id="svg5" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="dock-station-right-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview7" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="true" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="false" + inkscape:zoom="4.4555556" + inkscape:cx="86.408977" + inkscape:cy="89.999999" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" /> + <defs + id="defs2" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + id="rect254" + style="fill:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke;fill-opacity:1" + d="M 30 15 C 13.380034 15 0 28.380034 0 45 L 0 135 C 0 151.61996 13.380034 165 30 165 L 105 165 L 115 165 L 150 165 C 166.61996 165 180 151.61996 180 135 L 180 45 C 180 28.380034 166.61996 15 150 15 L 115 15 L 105 15 L 30 15 z M 35 35 L 105 35 L 105 145 L 35 145 C 26.690016 145 20 138.30998 20 130 L 20 50 C 20 41.690016 26.690016 35 35 35 z M 115 35 L 145 35 C 153.30998 35 160 41.690016 160 50 L 160 130 C 160 138.30998 153.30998 145 145 145 L 115 145 L 115 35 z " /> + <path + id="rect390" + style="fill:#000000;fill-opacity:0.41999999;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 115 35 L 115 145 L 145 145 C 153.30999 145 160 138.30999 160 130 L 160 50 C 160 41.690008 153.30999 35 145 35 L 115 35 z " /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Calque 2" + style="display:none;opacity:0.700936"> + <g + fill="#2e3436" + id="g182" + transform="matrix(11.328515,0,0,10.683311,-1.25624,4.6587097)" + style="stroke-width:0.0908993"> + <path + d="m 6.5,13.988281 v -12 h -5 v 12 z m 0,0" + fill-opacity="0.35" + id="path176" + style="stroke-width:0.00826268" /> + <path + d="m 3,0.988281 c -1.644531,0 -3,1.355469 -3,3 v 8 c 0,1.644531 1.355469,3 3,3 h 10 c 1.644531,0 3,-1.355469 3,-3 v -8 c 0,-1.644531 -1.355469,-3 -3,-3 z m 0,2 h 10 c 0.570312,0 1,0.429688 1,1 v 8 c 0,0.570313 -0.429688,1 -1,1 H 3 c -0.570312,0 -1,-0.429687 -1,-1 v -8 c 0,-0.570312 0.429688,-1 1,-1 z m 0,0" + id="path178" + style="stroke-width:0.0908993" /> + <path + d="m 6,1.988281 h 1 v 12 H 6 Z m 0,0" + id="path180" + style="stroke-width:0.0908993" /> + </g> + </g> +</svg> diff --git a/src/data/images/gresource.xml b/src/data/images/gresource.xml new file mode 100644 index 0000000..25c8274 --- /dev/null +++ b/src/data/images/gresource.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/re/chrysalide/framework/gui/dialogs/about"> + <file compressed="true" alias="bg.png">about-bg.png</file> + </gresource> + <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions"> + <file compressed="true">dock-station-left-symbolic.svg</file> + <file compressed="true">dock-station-right-symbolic.svg</file> + <file compressed="true">dock-station-bottom-symbolic.svg</file> + <file compressed="true">logs-symbolic.svg</file> + </gresource> +</gresources> diff --git a/src/data/images/logs-symbolic.svg b/src/data/images/logs-symbolic.svg new file mode 100644 index 0000000..f541607 --- /dev/null +++ b/src/data/images/logs-symbolic.svg @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="180" + height="180" + viewBox="0 0 180 180" + version="1.1" + id="svg5" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="logs-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview7" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="false" + inkscape:zoom="3.9657403" + inkscape:cx="110.44596" + inkscape:cy="77.413036" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" /> + <defs + id="defs2" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1" + style="fill:#25ff4c;fill-opacity:1"> + <path + id="rect495" + style="opacity:1;fill:#000000;fill-opacity:1;stroke-width:1.99999;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 45 0 C 31.150014 0 20 11.150014 20 25 L 20 155 C 20 168.84999 31.150014 180 45 180 L 155 180 C 168.84999 180 180 168.84999 180 155 L 180 25 C 180 11.150014 168.84999 0 155 0 L 45 0 z M 55 10 L 145 10 C 158.84999 10 170 21.150014 170 35 L 170 145 C 170 158.84999 158.84999 170 145 170 L 55 170 C 41.150014 170 30 158.84999 30 145 L 30 35 C 30 21.150014 41.150014 10 55 10 z " /> + <rect + style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + id="rect3619" + width="75" + height="12" + x="62.5" + y="84" + rx="6" + ry="6" /> + <rect + style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + id="rect5029" + width="75" + height="12" + x="62.5" + y="127" + rx="6" + ry="6" /> + <rect + style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + id="rect5031" + width="75" + height="12" + x="62.5" + y="41" + rx="6" + ry="6" /> + <rect + style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + id="rect5049" + width="34" + height="12" + x="8.2744884" + y="41" + rx="6" + ry="6" /> + <rect + style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + id="rect5051" + width="34" + height="12" + x="8.2744884" + y="127" + rx="6" + ry="6" /> + </g> +</svg> diff --git a/src/format/Makefile.am b/src/format/Makefile.am index 305cd92..3ffe24e 100644 --- a/src/format/Makefile.am +++ b/src/format/Makefile.am @@ -1,23 +1,25 @@ noinst_LTLIBRARIES = libformat.la +# debuggable-int.h \ +# debuggable.h debuggable.c \ +# executable-int.c \ +# preload-int.h \ +# preload.h preload.c \ +# strsym.h strsym.c \ +# symiter.h symiter.c \ +# symbol-int.h \ +# symbol.h symbol.c + libformat_la_SOURCES = \ - debuggable-int.h \ - debuggable.h debuggable.c \ - executable-int.h executable-int.c \ + executable-int.h \ executable.h executable.c \ flat-int.h \ flat.h flat.c \ - format-int.h \ - format.h format.c \ known-int.h \ known.h known.c \ - preload-int.h \ - preload.h preload.c \ - strsym.h strsym.c \ - symiter.h symiter.c \ - symbol-int.h \ - symbol.h symbol.c + program-int.h \ + program.h program.c libformat_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) diff --git a/src/format/executable-int.h b/src/format/executable-int.h index 8722795..1ca1bdb 100644 --- a/src/format/executable-int.h +++ b/src/format/executable-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * executable-int.h - prototypes de code utile aux formats d'exécutables * - * Copyright (C) 2009-2019 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,79 +28,65 @@ #include "executable.h" -#include "format-int.h" +#include "program-int.h" /* Indique le type d'architecture visée par le format. */ -typedef const char * (* get_target_machine_fc) (const GExeFormat *); +typedef char * (* get_target_machine_fc) (const GExecutableFormat *); /* Fournit l'adresse principale associée à un format. */ -typedef bool (* get_main_addr_fc) (GExeFormat *, vmpa2t *); +typedef bool (* get_main_addr_fc) (GExecutableFormat *, vmpa2t *); /* Etend la définition des portions au sein d'un binaire. */ -typedef void (* refine_portions_fc) (GExeFormat *); +typedef bool (* refine_portions_fc) (GExecutableFormat *); /* Fournit l'emplacement correspondant à une position physique. */ -typedef bool (* translate_phys_fc) (GExeFormat *, phys_t, vmpa2t *); +typedef bool (* translate_phys_fc) (GExecutableFormat *, phys_t, vmpa2t *); /* Fournit l'emplacement correspondant à une adresse virtuelle. */ -typedef bool (* translate_virt_fc) (GExeFormat *, virt_t, vmpa2t *); - -/* Fournit l'emplacement d'une section donnée. */ -typedef bool (* get_range_by_name_fc) (const GExeFormat *, const char *, mrange_t *); - +typedef bool (* translate_virt_fc) (GExecutableFormat *, virt_t, vmpa2t *); /* Format d'exécutable générique (instance) */ -struct _GExeFormat +struct _GExecutableFormat { - GBinFormat parent; /* A laisser en premier */ + GProgramFormat parent; /* A laisser en premier */ - GDbgFormat **debugs; /* Informations de débogage */ - size_t debugs_count; /* Nombre de ces informations */ - - GBinPortion **user_portions; /* Couches de morceaux binaires*/ - size_t user_count; /* Nombre de ces portions */ - GBinPortion *portions; /* Couches de morceaux binaires*/ - GMutex mutex; /* Accès à l'arborescence */ + GBinaryPortion *portions; /* Couches de morceaux binaires*/ }; /* Format d'exécutable générique (classe) */ -struct _GExeFormatClass +struct _GExecutableFormatClass { - GBinFormatClass parent; /* A laisser en premier */ + GProgramFormatClass parent; /* A laisser en premier */ get_target_machine_fc get_machine; /* Architecture ciblée */ + get_main_addr_fc get_main_addr; /* Obtention d'adresse première*/ refine_portions_fc refine_portions; /* Décrit les portions binaires*/ translate_phys_fc translate_phys; /* Correspondance phys -> vmpa */ translate_virt_fc translate_virt; /* Correspondance virt -> vmpa */ - get_range_by_name_fc get_range_by_name; /* Emplacement de sections */ - }; -/* Crée les portions potentiellement utiles aux traductions. */ -void g_executable_format_setup_portions(GExeFormat *, GtkStatusStack *); +/* Met en place un nouveau contenu binaire à analyser. */ +bool g_executable_format_create(GExecutableFormat *, GBinContent *); -/* Effectue les ultimes opérations de chargement d'un binaire. */ -bool g_executable_format_complete_loading(GExeFormat *, wgroup_id_t, GtkStatusStack *); +/* Fournit l'emplacement correspondant à une position physique. */ +bool g_executable_format_translate_offset_into_vmpa_without_virt(const GExecutableFormat *, phys_t, vmpa2t *); /* Fournit l'emplacement correspondant à une position physique. */ -bool g_exe_format_without_virt_translate_offset_into_vmpa(const GExeFormat *, phys_t, vmpa2t *); +bool g_executable_format_translate_offset_into_vmpa_with_portions(GExecutableFormat *, phys_t, vmpa2t *); /* Fournit l'emplacement correspondant à une adresse virtuelle. */ -bool g_exe_format_without_virt_translate_address_into_vmpa(const GExeFormat *, virt_t, vmpa2t *); - -/* Fournit l'emplacement correspondant à une position physique. */ -bool g_exe_format_translate_offset_into_vmpa_using_portions(GExeFormat *, phys_t, vmpa2t *); +bool g_executable_format_translate_address_into_vmpa_without_virt(const GExecutableFormat *, virt_t, vmpa2t *); /* Fournit l'emplacement correspondant à une adresse virtuelle. */ -bool g_exe_format_translate_address_into_vmpa_using_portions(GExeFormat *, virt_t, vmpa2t *); +bool g_executable_format_translate_address_into_vmpa_with_portions(GExecutableFormat *, virt_t, vmpa2t *); diff --git a/src/format/executable.c b/src/format/executable.c index 26c418e..d0fc3e2 100644 --- a/src/format/executable.c +++ b/src/format/executable.c @@ -27,35 +27,48 @@ #include <assert.h> #include <malloc.h> #include <stdio.h> -#include <stdlib.h> #include <i18n.h> #include "executable-int.h" -#include "format.h" #include "../core/logs.h" -#include "../plugins/pglist.h" +/* ------------------------- GESTION D'UN FORMAT EXECUTABLE ------------------------- */ + + /* Initialise la classe des formats d'exécutables génériques. */ -static void g_executable_format_class_init(GExeFormatClass *); +static void g_executable_format_class_init(GExecutableFormatClass *); /* Initialise une instance de format d'exécutable générique. */ -static void g_executable_format_init(GExeFormat *); +static void g_executable_format_init(GExecutableFormat *); /* Supprime toutes les références externes. */ -static void g_executable_format_dispose(GExeFormat *); +static void g_executable_format_dispose(GExecutableFormat *); /* Procède à la libération totale de la mémoire. */ -static void g_executable_format_finalize(GExeFormat *); +static void g_executable_format_finalize(GExecutableFormat *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Assure l'interprétation d'un format en différé. */ +static bool g_executable_format_analyze(GExecutableFormat *); + +/* ---------------------------------------------------------------------------------- */ +/* GESTION D'UN FORMAT EXECUTABLE */ +/* ---------------------------------------------------------------------------------- */ + /* Indique le type défini pour un format d'exécutable générique. */ -G_DEFINE_TYPE(GExeFormat, g_executable_format, G_TYPE_BIN_FORMAT); +G_DEFINE_TYPE(GExecutableFormat, g_executable_format, G_TYPE_PROGRAM_FORMAT); /****************************************************************************** @@ -70,15 +83,20 @@ G_DEFINE_TYPE(GExeFormat, g_executable_format, G_TYPE_BIN_FORMAT); * * ******************************************************************************/ -static void g_executable_format_class_init(GExeFormatClass *klass) +static void g_executable_format_class_init(GExecutableFormatClass *klass) { GObjectClass *object; /* Autre version de la classe */ + GKnownFormatClass *known; /* Version de format connu */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_executable_format_dispose; object->finalize = (GObjectFinalizeFunc)g_executable_format_finalize; + known = G_KNOWN_FORMAT_CLASS(klass); + + known->analyze = (known_analyze_fc)g_executable_format_analyze; + } @@ -94,9 +112,9 @@ static void g_executable_format_class_init(GExeFormatClass *klass) * * ******************************************************************************/ -static void g_executable_format_init(GExeFormat *format) +static void g_executable_format_init(GExecutableFormat *format) { - g_mutex_init(&format->mutex); + format->portions = NULL; } @@ -113,20 +131,10 @@ static void g_executable_format_init(GExeFormat *format) * * ******************************************************************************/ -static void g_executable_format_dispose(GExeFormat *format) +static void g_executable_format_dispose(GExecutableFormat *format) { - size_t i; /* Boucle de parcours */ - - for (i = 0; i < format->debugs_count; i++) - g_clear_object(&format->debugs[i]); - - for (i = 0; i < format->user_count; i++) - g_clear_object(&format->user_portions[i]); - g_clear_object(&format->portions); - g_mutex_clear(&format->mutex); - G_OBJECT_CLASS(g_executable_format_parent_class)->dispose(G_OBJECT(format)); } @@ -144,14 +152,8 @@ static void g_executable_format_dispose(GExeFormat *format) * * ******************************************************************************/ -static void g_executable_format_finalize(GExeFormat *format) +static void g_executable_format_finalize(GExecutableFormat *format) { - if (format->debugs != NULL) - free(format->debugs); - - if (format->user_portions != NULL) - free(format->user_portions); - G_OBJECT_CLASS(g_executable_format_parent_class)->finalize(G_OBJECT(format)); } @@ -159,79 +161,66 @@ static void g_executable_format_finalize(GExeFormat *format) /****************************************************************************** * * -* Paramètres : format = informations chargées à compléter. * -* info = informations de débogage à lier. * +* Paramètres : format = description du format à initialiser pleinement. * +* content = contenu binaire à parcourir. * * * -* Description : Rajoute des informations de débogage à un exécutable. * +* Description : Met en place un nouveau contenu binaire à analyser. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -void g_exe_format_add_debug_info(GExeFormat *format, GDbgFormat *info) +bool g_executable_format_create(GExecutableFormat *format, GBinContent *content) { - const char *desc; /* Description humaine associée*/ + bool result; /* Bilan à retourner */ + vmpa2t addr; /* Emplacement vide de sens */ + phys_t length; /* Taille de portion globale */ - desc = g_known_format_get_description(G_KNOWN_FORMAT(info)); + result = g_program_format_create(G_PROGRAM_FORMAT(format), content); + if (!result) goto exit; - if (desc == NULL) - log_simple_message(LMT_WARNING, _("Unnamed debug information")); - else - log_variadic_message(LMT_INFO, _("Found debug information: %s"), desc); + /* Définition de portions */ - format->debugs = realloc(format->debugs, ++format->debugs_count * sizeof(GDbgFormat *)); - - format->debugs[format->debugs_count - 1] = info; + /** + * Avant de lire l'entête du format, on ne sait pas où on se trouve ! + */ + init_vmpa(&addr, 0, VMPA_NO_VIRTUAL); -} + length = g_binary_content_compute_size(G_KNOWN_FORMAT(format)->content); + format->portions = g_binary_portion_new(&addr, length); -/****************************************************************************** -* * -* Paramètres : format = informations chargées à consulter. * -* * -* Description : Compte le nombre de formats de débogage liés à l'exécutable. * -* * -* Retour : Nombre de formats de débogage attachés. * -* * -* Remarques : - * -* * -******************************************************************************/ + exit: -size_t g_exe_format_count_debug_info(const GExeFormat *format) -{ - return format->debugs_count; + return result; } /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * -* index = indice des informations à transmettre. * +* Paramètres : format = description du format exécutable à consulter. * * * -* Description : Fournit un format de débogage attaché à l'exécutable. * +* Description : Indique le type d'architecture visée par le format. * * * -* Retour : Informations de débogage attachées. * +* Retour : Identifiant de l'architecture ciblée par le format. * * * * Remarques : - * * * ******************************************************************************/ -GDbgFormat *g_exe_format_get_debug_info(const GExeFormat *format, size_t index) +char *g_executable_format_get_target_machine(const GExecutableFormat *format) { - GDbgFormat *result; /* Format à retourner */ + char *result; /* Désignation à retourner */ + GExecutableFormatClass *class; /* Classe de l'instance */ - if (index >= format->debugs_count) - result = NULL; + class = G_EXECUTABLE_FORMAT_GET_CLASS(format); - else - { - result = format->debugs[index]; - g_object_ref(G_OBJECT(result)); - } + result = class->get_machine(format); + + //assert(result != NULL); return result; @@ -240,56 +229,96 @@ GDbgFormat *g_exe_format_get_debug_info(const GExeFormat *format, size_t index) /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * +* Paramètres : format = description de l'exécutable à consulter. * +* addr = adresse principale trouvée si possible. [OUT] * * * -* Description : Indique le type d'architecture visée par le format. * +* Description : Fournit l'adresse principale associée à un format. * * * -* Retour : Identifiant de l'architecture ciblée par le format. * +* Retour : Validité de l'adresse transmise. * * * * Remarques : - * * * ******************************************************************************/ -const char *g_exe_format_get_target_machine(const GExeFormat *format) +bool g_executable_format_get_main_address(GExecutableFormat *format, vmpa2t *addr) { - return G_EXE_FORMAT_GET_CLASS(format)->get_machine(format); + bool result; /* Bilan à retourner */ + GExecutableFormatClass *class; /* Classe de l'instance */ + + class = G_EXECUTABLE_FORMAT_GET_CLASS(format); + + result = class->get_main_addr(format, addr); + + return result; } /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* addr = adresse principale trouvée si possible. [OUT] * +* Paramètres : format = description de l'exécutable à modifier. * +* portion = portion à inclure dans les définitions du format. * +* origin = source de définition de la portion fournie. * * * -* Description : Fournit l'adresse principale associée à un format. * +* Description : Procède à l'enregistrement d'une portion dans un format. * * * -* Retour : Bilan des recherches. * +* Retour : Bilan de l'opération : true si inclusion, false sinon. * * * * Remarques : - * * * ******************************************************************************/ -bool g_exe_format_get_main_address(GExeFormat *format, vmpa2t *addr) +bool g_executable_format_include_portion(GExecutableFormat *format, GBinaryPortion *portion, const vmpa2t *origin) { bool result; /* Bilan à retourner */ - GBinFormat *base; /* Version d'instance parente */ + phys_t available; /* Taille totale du bianire */ + const mrange_t *range; /* Emplacement de la portion */ + phys_t start; /* Début de zone de la portion */ + vmpa2t no_origin; /* Emplacement inconnu */ + char *msg; /* Description d'une erreur */ + phys_t remaining; /* Taille maximale envisageable*/ + bool truncated; /* Modification faite ? */ result = false; - if (G_EXE_FORMAT_GET_CLASS(format)->get_main_addr != NULL) - result = G_EXE_FORMAT_GET_CLASS(format)->get_main_addr(format, addr); + available = g_binary_content_compute_size(G_KNOWN_FORMAT(format)->content); + + range = g_binary_portion_get_range(portion); + + start = get_phy_addr(get_mrange_addr(range)); + + if (get_mrange_length(range) == 0) + log_variadic_message(LMT_BAD_BINARY, _("The binary portion '%s' is empty and thus useless... Discarding!"), + g_binary_portion_get_desc(portion)); - if (!result) + else if (start >= available) { - base = G_BIN_FORMAT(format); + if (origin == NULL) + { + init_vmpa(&no_origin, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + origin = &no_origin; + } - g_rw_lock_reader_lock(&base->pt_lock); + asprintf(&msg, _("Defined binary portion '%s' is out of the file scope... Discarding!"), + g_binary_portion_get_desc(portion)); - if (base->pt_count[DPL_ENTRY_POINT] > 0) - result = g_exe_format_translate_address_into_vmpa(format, base->start_points[DPL_ENTRY_POINT][0], addr); + //g_binary_format_add_error(G_BIN_FORMAT(format), BFE_STRUCTURE, origin, msg); - g_rw_lock_reader_unlock(&base->pt_lock); + free(msg); + + } + + else + { + remaining = available - start; + + truncated = g_binary_portion_limit_range(portion, remaining); + + if (truncated) + log_variadic_message(LMT_BAD_BINARY, _("Truncated binary portion '%s' to fit the binary content size!"), + g_binary_portion_get_desc(portion)); + + result = g_binary_portion_include(format->portions, portion); } @@ -300,54 +329,39 @@ bool g_exe_format_get_main_address(GExeFormat *format, vmpa2t *addr) /****************************************************************************** * * -* Paramètres : format = instance à traiter. * -* status = barre de statut à tenir informée. * +* Paramètres : format = description de l'exécutable à consulter. * * * -* Description : Crée les portions potentiellement utiles aux traductions. * +* Description : Fournit la première couche des portions composent le binaire.* * * -* Retour : - * +* Retour : Arborescence des différentes portions binaires. * * * -* Remarques : - * +* Remarques : Le compteur de références de l'instance renvoyée doit être * +* décrémenté après usage. * * * ******************************************************************************/ -void g_executable_format_setup_portions(GExeFormat *format, GtkStatusStack *status) +GBinaryPortion *g_executable_format_get_portions(GExecutableFormat *format) { - vmpa2t addr; /* Emplacement vide de sens */ - phys_t length; /* Taille de portion globale */ - GExeFormatClass *class; /* Classe de l'instance */ - size_t i; /* Boucle de parcours */ - - /** - * Avant de lire l'entête du format, on ne sait pas où on se trouve ! - */ - init_vmpa(&addr, 0, VMPA_NO_VIRTUAL); + GBinaryPortion *result; /* Instance à retourner */ - length = g_binary_content_compute_size(G_KNOWN_FORMAT(format)->content); - - format->portions = g_binary_portion_new(BPC_RAW, &addr, length); + result = format->portions; - class = G_EXE_FORMAT_GET_CLASS(format); + assert(result != NULL); - if (class->refine_portions != NULL) - class->refine_portions(format); + ref_object(result); - for (i = 0; i < format->user_count; i++) - { - g_object_ref(G_OBJECT(format->user_portions[i])); - g_exe_format_include_portion(format, format->user_portions[i], NULL); - } + return result; } /****************************************************************************** * * -* Paramètres : format = instance à traiter. * -* gid = groupe de travail dédié. * -* status = barre de statut à tenir informée. * +* Paramètres : format = description de l'exécutable à consulter. * +* off = position physique à retrouver. * +* pos = position correspondante. [OUT] * * * -* Description : Effectue les ultimes opérations de chargement d'un binaire. * +* Description : Fournit l'emplacement correspondant à une position physique. * * * * Retour : Bilan de l'opération. * * * @@ -355,28 +369,14 @@ void g_executable_format_setup_portions(GExeFormat *format, GtkStatusStack *stat * * ******************************************************************************/ -bool g_executable_format_complete_loading(GExeFormat *format, wgroup_id_t gid, GtkStatusStack *status) +bool g_executable_format_translate_offset_into_vmpa(GExecutableFormat *format, phys_t off, vmpa2t *pos) { - bool result; /* Bilan à faire remonter */ - size_t count; /* Qté d'infos supplémentaires */ - size_t i; /* Boucle de parcours */ - GDbgFormat *dbg; /* Informations de débogage */ - - result = true; - - attach_debug_format(format); - - count = g_exe_format_count_debug_info(format); - - for (i = 0; i < count && result; i++) - { - dbg = g_exe_format_get_debug_info(format, i); - - result = g_known_format_analyze(G_KNOWN_FORMAT(dbg), gid, status); + bool result; /* Bilan à retourner */ + GExecutableFormatClass *class; /* Classe de l'instance */ - g_object_unref(G_OBJECT(dbg)); + class = G_EXECUTABLE_FORMAT_GET_CLASS(format); - } + result = class->translate_phys(format, off, pos); return result; @@ -385,105 +385,52 @@ bool g_executable_format_complete_loading(GExeFormat *format, wgroup_id_t gid, G /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à modifier. * -* portion = portion à inclure dans les définitions du format. * +* Paramètres : format = description de l'exécutable à consulter. * +* off = position physique à retrouver. * +* pos = position correspondante. [OUT] * * * -* Description : Enregistre une portion artificielle pour le format. * +* Description : Fournit l'emplacement correspondant à une position physique. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -void g_exe_format_register_user_portion(GExeFormat *format, GBinPortion *portion) +bool g_executable_format_translate_offset_into_vmpa_without_virt(const GExecutableFormat *format, phys_t off, vmpa2t *pos) { - g_mutex_lock(&format->mutex); + init_vmpa(pos, off, VMPA_NO_VIRTUAL); - format->user_portions = realloc(format->user_portions, ++format->user_count * sizeof(GBinPortion *)); - - format->user_portions[format->user_count - 1] = portion; - - g_mutex_unlock(&format->mutex); + return true; } /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à modifier. * -* portion = portion à inclure dans les définitions du format. * -* origin = source de définition de la portion fournie. * +* Paramètres : format = description de l'exécutable à consulter. * +* off = position physique à retrouver. * +* pos = position correspondante. [OUT] * * * -* Description : Procède à l'enregistrement d'une portion dans un format. * +* Description : Fournit l'emplacement correspondant à une position physique. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -void g_exe_format_include_portion(GExeFormat *format, GBinPortion *portion, const vmpa2t *origin) +bool g_executable_format_translate_offset_into_vmpa_with_portions(GExecutableFormat *format, phys_t off, vmpa2t *pos) { - phys_t available; /* Taille totale du bianire */ - const mrange_t *range; /* Emplacement de la portion */ - phys_t start; /* Début de zone de la portion */ - vmpa2t no_origin; /* Emplacement inconnu */ - char *msg; /* Description d'une erreur */ - phys_t remaining; /* Taille maximale envisageable*/ - bool truncated; /* Modification faite ? */ - - available = g_binary_content_compute_size(G_KNOWN_FORMAT(format)->content); - - range = g_binary_portion_get_range(portion); - - start = get_phy_addr(get_mrange_addr(range)); - - if (get_mrange_length(range) == 0) - { - log_variadic_message(LMT_BAD_BINARY, _("The binary portion '%s' is empty and thus useless... Discarding!"), - g_binary_portion_get_desc(portion)); - - g_object_unref(G_OBJECT(portion)); - - } - - else if (start >= available) - { - if (origin == NULL) - { - init_vmpa(&no_origin, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); - origin = &no_origin; - } - - asprintf(&msg, _("Defined binary portion '%s' is out of the file scope... Discarding!"), - g_binary_portion_get_desc(portion)); - - g_binary_format_add_error(G_BIN_FORMAT(format), BFE_STRUCTURE, origin, msg); - - free(msg); - - g_object_unref(G_OBJECT(portion)); + bool result; /* Bilan à retourner */ - } + if (format->portions == NULL) + result = false; else - { - remaining = available - start; - - truncated = g_binary_portion_limit_range(portion, remaining); - - if (truncated) - log_variadic_message(LMT_BAD_BINARY, _("Truncated binary portion '%s' to fit the binary content size!"), - g_binary_portion_get_desc(portion)); + result = g_binary_portion_translate_offset_into_vmpa(format->portions, off, pos); - g_mutex_lock(&format->mutex); - - g_binary_portion_include(format->portions, portion); - - g_mutex_unlock(&format->mutex); - - } + return result; } @@ -491,28 +438,25 @@ void g_exe_format_include_portion(GExeFormat *format, GBinPortion *portion, cons /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * +* addr = adresse virtuelle à retrouver. * +* pos = position correspondante. [OUT] * * * -* Description : Fournit la première couche des portions composent le binaire.* +* Description : Fournit l'emplacement correspondant à une adresse virtuelle. * * * -* Retour : Arborescence des différentes portions binaires. * +* Retour : Bilan de l'opération. * * * -* Remarques : Le compteur de références de l'instance renvoyée doit être * -* décrémenté après usage. * +* Remarques : - * * * ******************************************************************************/ -GBinPortion *g_exe_format_get_portions(GExeFormat *format) +bool g_executable_format_translate_address_into_vmpa(GExecutableFormat *format, virt_t addr, vmpa2t *pos) { - GBinPortion *result; /* Instance à retourner */ - - g_mutex_lock(&format->mutex); - - result = format->portions; + bool result; /* Bilan à retourner */ + GExecutableFormatClass *class; /* Classe de l'instance */ - if (result != NULL) - g_object_ref(G_OBJECT(result)); + class = G_EXECUTABLE_FORMAT_GET_CLASS(format); - g_mutex_unlock(&format->mutex); + result = class->translate_virt(format, addr, pos); return result; @@ -522,10 +466,10 @@ GBinPortion *g_exe_format_get_portions(GExeFormat *format) /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * -* off = position physique à retrouver. * +* addr = adresse virtuelle à retrouver. * * pos = position correspondante. [OUT] * * * -* Description : Fournit l'emplacement correspondant à une position physique. * +* Description : Fournit l'emplacement correspondant à une adresse virtuelle. * * * * Retour : Bilan de l'opération. * * * @@ -533,13 +477,21 @@ GBinPortion *g_exe_format_get_portions(GExeFormat *format) * * ******************************************************************************/ -bool g_exe_format_translate_offset_into_vmpa(GExeFormat *format, phys_t off, vmpa2t *pos) +bool g_executable_format_translate_address_into_vmpa_without_virt(const GExecutableFormat *format, virt_t addr, vmpa2t *pos) { - bool result; /* Bilan à retourner */ + /** + * S'il n'y a pas de notion de mémoire virtuelle, positions physiques et + * adresses virtuelles se confondent. + * + * On reste néanmoins cohérent, et on n'utilise donc pas d'adresse virtuelle. + * + * Les sauts dans le code renvoient de façon transparente vers des positions + * physiques. + */ - result = G_EXE_FORMAT_GET_CLASS(format)->translate_phys(format, off, pos); + init_vmpa(pos, addr, VMPA_NO_VIRTUAL); - return result; + return true; } @@ -558,24 +510,32 @@ bool g_exe_format_translate_offset_into_vmpa(GExeFormat *format, phys_t off, vmp * * ******************************************************************************/ -bool g_exe_format_translate_address_into_vmpa(GExeFormat *format, virt_t addr, vmpa2t *pos) +bool g_executable_format_translate_address_into_vmpa_with_portions(GExecutableFormat *format, virt_t addr, vmpa2t *pos) { bool result; /* Bilan à retourner */ - result = G_EXE_FORMAT_GET_CLASS(format)->translate_virt(format, addr, pos); + if (format->portions == NULL) + result = false; + + else + result = g_binary_portion_translate_address_into_vmpa(format->portions, addr, pos); return result; } + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* name = nom de la section recherchée. * -* range = emplacement en mémoire à renseigner. [OUT] * +* Paramètres : format = format chargé dont l'analyse est lancée. * * * -* Description : Fournit l'emplacement d'une section donnée. * +* Description : Assure l'interprétation d'un format en différé. * * * * Retour : Bilan de l'opération. * * * @@ -583,18 +543,17 @@ bool g_exe_format_translate_address_into_vmpa(GExeFormat *format, virt_t addr, v * * ******************************************************************************/ -bool g_exe_format_get_section_range_by_name(const GExeFormat *format, const char *name, mrange_t *range) +static bool g_executable_format_analyze(GExecutableFormat *format) { bool result; /* Bilan à retourner */ - GExeFormatClass *class; /* Classe de l'instance */ + GExecutableFormatClass *class; /* Classe de l'instance */ - class = G_EXE_FORMAT_GET_CLASS(format); - - if (class->get_range_by_name == NULL) - result = false; + class = G_EXECUTABLE_FORMAT_GET_CLASS(format); + if (class->refine_portions != NULL) + result = class->refine_portions(format); else - result = class->get_range_by_name(format, name, range); + result = true; return result; diff --git a/src/format/executable.h b/src/format/executable.h index 4f5abc7..da71d66 100644 --- a/src/format/executable.h +++ b/src/format/executable.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * executable.h - prototypes pour le support des formats d'exécutables * - * Copyright (C) 2009-2019 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,88 +25,56 @@ #define _FORMAT_EXECUTABLE_H -#include <glib-object.h> +#include <stdbool.h> -#include "debuggable.h" -#include "../glibext/gbinportion.h" +#include "../glibext/helpers.h" +#include "../glibext/portion.h" -#define G_TYPE_EXE_FORMAT g_executable_format_get_type() -#define G_EXE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_EXE_FORMAT, GExeFormat)) -#define G_IS_EXE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_EXE_FORMAT)) -#define G_EXE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_EXE_FORMAT, GExeFormatClass)) -#define G_IS_EXE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_EXE_FORMAT)) -#define G_EXE_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_EXE_FORMAT, GExeFormatClass)) +#define G_TYPE_EXECUTABLE_FORMAT (g_executable_format_get_type()) +DECLARE_GTYPE(GExecutableFormat, g_executable_format, G, EXECUTABLE_FORMAT); -/* Format d'exécutable générique (instance) */ -typedef struct _GExeFormat GExeFormat; - -/* Format d'exécutable générique (classe) */ -typedef struct _GExeFormatClass GExeFormatClass; - - -/* Indique le type défini pour un format d'exécutable générique. */ -GType g_executable_format_get_type(void); - -/* Rajoute des informations de débogage à un exécutable. */ -void g_exe_format_add_debug_info(GExeFormat *, GDbgFormat *); - -/* Compte le nombre de formats de débogage liés à l'exécutable. */ -size_t g_exe_format_count_debug_info(const GExeFormat *); - -/* Fournit un format de débogage attaché à l'exécutable. */ -GDbgFormat *g_exe_format_get_debug_info(const GExeFormat *, size_t); /* Indique le type d'architecture visée par le format. */ -const char *g_exe_format_get_target_machine(const GExeFormat *); +char *g_executable_format_get_target_machine(const GExecutableFormat *); /* Fournit l'adresse principale associée à un format. */ -bool g_exe_format_get_main_address(GExeFormat *, vmpa2t *); - -/* Enregistre une portion artificielle pour le format. */ -void g_exe_format_register_user_portion(GExeFormat *, GBinPortion *); +bool g_executable_format_get_main_address(GExecutableFormat *, vmpa2t *); /* Procède à l'enregistrement d'une portion dans un format. */ -void g_exe_format_include_portion(GExeFormat *, GBinPortion *, const vmpa2t *); +bool g_executable_format_include_portion(GExecutableFormat *, GBinaryPortion *, const vmpa2t *); /* Fournit la première couche des portions composent le binaire. */ -GBinPortion *g_exe_format_get_portions(GExeFormat *); - -/* Fournit les espaces mémoires des portions exécutables. */ -mrange_t *g_exe_format_get_x_ranges(GExeFormat *, size_t *); +GBinaryPortion *g_executable_format_get_portions(GExecutableFormat *); /* Fournit l'emplacement correspondant à une position physique. */ -bool g_exe_format_translate_offset_into_vmpa(GExeFormat *, phys_t, vmpa2t *); +bool g_executable_format_translate_offset_into_vmpa(GExecutableFormat *, phys_t, vmpa2t *); /* Fournit l'emplacement correspondant à une position physique. */ -bool g_exe_format_translate_address_into_vmpa(GExeFormat *, virt_t, vmpa2t *); +bool g_executable_format_translate_address_into_vmpa(GExecutableFormat *, virt_t, vmpa2t *); -#define g_exe_format_translate_offset_into_address(fmt, off, addr) \ - ({ \ - bool __result; \ - vmpa2t __pos; \ - __result = g_exe_format_translate_offset_into_vmpa(fmt, off, &__pos); \ - *addr = get_virt_addr(&__pos); \ - __result; \ +#define g_executable_format_translate_offset_into_address(fmt, off, addr) \ + ({ \ + bool __result; \ + vmpa2t __pos; \ + __result = g_executable_format_translate_offset_into_vmpa(fmt, off, &__pos); \ + *addr = get_virt_addr(&__pos); \ + __result; \ }) -#define g_exe_format_translate_address_into_offset(fmt, addr, off) \ - ({ \ - bool __result; \ - vmpa2t __pos; \ - __result = g_exe_format_translate_address_into_vmpa(fmt, addr, &__pos); \ - *off = get_phy_addr(&__pos); \ - __result; \ +#define g_executable_format_translate_address_into_offset(fmt, addr, off) \ + ({ \ + bool __result; \ + vmpa2t __pos; \ + __result = g_executable_format_translate_address_into_vmpa(fmt, addr, &__pos); \ + *off = get_phy_addr(&__pos); \ + __result; \ }) -/* Fournit l'emplacement d'une section donnée. */ -bool g_exe_format_get_section_range_by_name(const GExeFormat *, const char *, mrange_t *); - - #endif /* _FORMAT_EXECUTABLE_H */ diff --git a/src/format/flat-int.h b/src/format/flat-int.h index d785e5f..f858ba3 100644 --- a/src/format/flat-int.h +++ b/src/format/flat-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * flat-int.h - prototypes de code utile aux formats d'exécutables à plat * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -35,7 +35,7 @@ /* Format d'exécutable à plat (instance) */ struct _GFlatFormat { - GExeFormat parent; /* A laisser en premier */ + GExecutableFormat parent; /* A laisser en premier */ char *machine; /* Architecture imposée */ SourceEndian endian; /* Boutisme imposé */ @@ -45,10 +45,14 @@ struct _GFlatFormat /* Format d'exécutable à plat (classe) */ struct _GFlatFormatClass { - GExeFormatClass parent; /* A laisser en premier */ + GExecutableFormatClass parent; /* A laisser en premier */ }; +/* Met en place une nouvelle instance de format à plat. */ +bool g_flat_format_create(GFlatFormat *, GBinContent *, const char *, SourceEndian); + + #endif /* _FORMAT_FLAT_INT_H */ diff --git a/src/format/flat.c b/src/format/flat.c index 6b8b074..51f37fa 100644 --- a/src/format/flat.c +++ b/src/format/flat.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * flat.c - support des formats à plat * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,6 +32,9 @@ +/* ------------------------- DEFINITION D'UN NOUVEAU FORMAT ------------------------- */ + + /* Initialise la classe des formats d'exécutables à plat. */ static void g_flat_format_class_init(GFlatFormatClass *); @@ -44,28 +47,39 @@ static void g_flat_format_dispose(GFlatFormat *); /* Procède à la libération totale de la mémoire. */ static void g_flat_format_finalize(GFlatFormat *); + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + /* Indique la désignation interne du format. */ static char *g_flat_format_get_key(const GFlatFormat *); /* Fournit une description humaine du format. */ static char *g_flat_format_get_description(const GFlatFormat *); -/* Assure l'interprétation d'un format en différé. */ -static bool g_flat_format_analyze(GFlatFormat *, wgroup_id_t, GtkStatusStack *); - /* Informe quant au boutisme utilisé. */ static SourceEndian g_flat_format_get_endianness(const GFlatFormat *); /* Indique le type d'architecture visée par le format. */ -static const char *g_flat_format_get_target_machine(const GFlatFormat *); +static char *g_flat_format_get_target_machine(const GFlatFormat *); + +#if 0 /* Fournit l'adresse principale associée à un format à plat. */ static bool g_flat_format_get_main_address(GFlatFormat *, vmpa2t *); +#endif + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UN NOUVEAU FORMAT */ +/* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour un format d'exécutable à plat. */ -G_DEFINE_TYPE(GFlatFormat, g_flat_format, G_TYPE_EXE_FORMAT); +G_DEFINE_TYPE(GFlatFormat, g_flat_format, G_TYPE_EXECUTABLE_FORMAT); /****************************************************************************** @@ -84,8 +98,8 @@ static void g_flat_format_class_init(GFlatFormatClass *klass) { GObjectClass *object; /* Autre version de la classe */ GKnownFormatClass *known; /* Version de format connu */ - GBinFormatClass *fmt; /* Version en format basique */ - GExeFormatClass *exe; /* Version en exécutable */ + GProgramFormatClass *prgm; /* Version en format basique */ + GExecutableFormatClass *exe; /* Version en exécutable */ object = G_OBJECT_CLASS(klass); @@ -97,19 +111,18 @@ static void g_flat_format_class_init(GFlatFormatClass *klass) known->get_key = (known_get_key_fc)g_flat_format_get_key; known->get_desc = (known_get_desc_fc)g_flat_format_get_description; - known->analyze = (known_analyze_fc)g_flat_format_analyze; - - fmt = G_BIN_FORMAT_CLASS(klass); + prgm = G_PROGRAM_FORMAT_CLASS(klass); - fmt->get_endian = (format_get_endian_fc)g_flat_format_get_endianness; + prgm->get_endian = (program_get_endian_fc)g_flat_format_get_endianness; - exe = G_EXE_FORMAT_CLASS(klass); + exe = G_EXECUTABLE_FORMAT_CLASS(klass); exe->get_machine = (get_target_machine_fc)g_flat_format_get_target_machine; +#if 0 exe->get_main_addr = (get_main_addr_fc)g_flat_format_get_main_address; - - exe->translate_phys = (translate_phys_fc)g_exe_format_translate_offset_into_vmpa_using_portions; - exe->translate_virt = (translate_virt_fc)g_exe_format_translate_address_into_vmpa_using_portions; +#endif + exe->translate_phys = g_executable_format_translate_offset_into_vmpa_with_portions; + exe->translate_virt = g_executable_format_translate_address_into_vmpa_with_portions; } @@ -178,8 +191,10 @@ static void g_flat_format_finalize(GFlatFormat *format) /****************************************************************************** * * * Paramètres : content = contenu binaire à parcourir. * +* machine = architecture déterminée pour le code. * + endian = boutisme à observer pour les données. * * * -* Description : Prend en charge un nouveau format à plat. * +* Description : Crée une nouvelle instance de format à plat. * * * * Retour : Adresse de la structure mise en place ou NULL en cas d'échec.* * * @@ -187,62 +202,72 @@ static void g_flat_format_finalize(GFlatFormat *format) * * ******************************************************************************/ -GExeFormat *g_flat_format_new(GBinContent *content, const char *machine, SourceEndian endian) +GFlatFormat *g_flat_format_new(GBinContent *content, const char *machine, SourceEndian endian) { GFlatFormat *result; /* Structure à retourner */ result = g_object_new(G_TYPE_FLAT_FORMAT, NULL); - g_known_format_set_content(G_KNOWN_FORMAT(result), content); + if (!g_flat_format_create(result, content, machine, endian)) + g_clear_object(&result); - result->machine = strdup(machine); - result->endian = endian; - - return G_EXE_FORMAT(result); + return result; } /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * +* Paramètres : format = description du format connu à consulter. * +* content = contenu binaire à parcourir. * +* machine = architecture déterminée pour le code. * + endian = boutisme à observer pour les données. * * * -* Description : Indique la désignation interne du format. * +* Description : Met en place une nouvelle instance de format à plat. * * * -* Retour : Désignation du format. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static char *g_flat_format_get_key(const GFlatFormat *format) +bool g_flat_format_create(GFlatFormat *format, GBinContent *content, const char *machine, SourceEndian endian) { - char *result; /* Désignation à retourner */ + bool result; /* Bilan à retourner */ - result = strdup("flat"); + result = g_executable_format_create(G_EXECUTABLE_FORMAT(format), content); + + format->machine = strdup(machine); + format->endian = endian; return result; } + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * * -* Description : Fournit une description humaine du format. * +* Description : Indique la désignation interne du format. * * * -* Retour : Description du format. * +* Retour : Désignation du format. * * * * Remarques : - * * * ******************************************************************************/ -static char *g_flat_format_get_description(const GFlatFormat *format) +static char *g_flat_format_get_key(const GFlatFormat *format) { char *result; /* Désignation à retourner */ - result = strdup("Flat executable format"); + result = strdup("flat"); return result; @@ -251,25 +276,21 @@ static char *g_flat_format_get_description(const GFlatFormat *format) /****************************************************************************** * * -* Paramètres : format = format chargé dont l'analyse est lancée. * -* gid = groupe de travail dédié. * -* status = barre de statut à tenir informée. * +* Paramètres : format = description de l'exécutable à consulter. * * * -* Description : Assure l'interprétation d'un format en différé. * +* Description : Fournit une description humaine du format. * * * -* Retour : Bilan de l'opération. * +* Retour : Description du format. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_flat_format_analyze(GFlatFormat *format, wgroup_id_t gid, GtkStatusStack *status) +static char *g_flat_format_get_description(const GFlatFormat *format) { - bool result; /* Bilan à retourner */ - - result = true; + char *result; /* Désignation à retourner */ - g_executable_format_setup_portions(G_EXE_FORMAT(format), status); + result = strdup("Flat executable format"); return result; @@ -290,14 +311,18 @@ static bool g_flat_format_analyze(GFlatFormat *format, wgroup_id_t gid, GtkStatu static SourceEndian g_flat_format_get_endianness(const GFlatFormat *format) { - return format->endian; + SourceEndian result; /* Valeur à retourner */ + + result = format->endian; + + return result; } /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * +* Paramètres : format = description du format exécutable à consulter. * * * * Description : Indique le type d'architecture visée par le format. * * * @@ -307,17 +332,17 @@ static SourceEndian g_flat_format_get_endianness(const GFlatFormat *format) * * ******************************************************************************/ -static const char *g_flat_format_get_target_machine(const GFlatFormat *format) +static char *g_flat_format_get_target_machine(const GFlatFormat *format) { - const char *result; /* Identifiant à retourner */ + char *result; /* Identifiant à retourner */ - result = format->machine; + result = strdup(format->machine); return result; } - +#if 0 /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * @@ -350,3 +375,5 @@ static bool g_flat_format_get_main_address(GFlatFormat *format, vmpa2t *addr) return result; } + +#endif diff --git a/src/format/flat.h b/src/format/flat.h index 83c5e3c..2168221 100644 --- a/src/format/flat.h +++ b/src/format/flat.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * flat.h - prototypes pour le support des formats à plat * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,34 +25,19 @@ #define _FORMAT_FLAT_H -#include <glib-object.h> - - -#include "executable.h" #include "../analysis/content.h" +#include "../glibext/helpers.h" -#define G_TYPE_FLAT_FORMAT g_flat_format_get_type() -#define G_FLAT_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_FLAT_FORMAT, GFlatFormat)) -#define G_IS_FLAT_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_FLAT_FORMAT)) -#define G_FLAT_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_FLAT_FORMAT, GFlatFormatClass)) -#define G_IS_FLAT_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_FLAT_FORMAT)) -#define G_FLAT_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_FLAT_FORMAT, GFlatFormatClass)) - - -/* Format d'exécutable à plat (instance) */ -typedef struct _GFlatFormat GFlatFormat; +#define G_TYPE_FLAT_FORMAT (g_flat_format_get_type()) -/* Format d'exécutable à plat (classe) */ -typedef struct _GFlatFormatClass GFlatFormatClass; +DECLARE_GTYPE(GFlatFormat, g_flat_format, G, FLAT_FORMAT); -/* Indique le type défini pour un format d'exécutable à plat. */ -GType g_flat_format_get_type(void); -/* Prend en charge un nouveau format à plat. */ -GExeFormat *g_flat_format_new(GBinContent *, const char *, SourceEndian); +/* Crée une nouvelle instance de format à plat. */ +GFlatFormat *g_flat_format_new(GBinContent *, const char *, SourceEndian); diff --git a/src/format/known-int.h b/src/format/known-int.h index 3328a96..75d0dab 100644 --- a/src/format/known-int.h +++ b/src/format/known-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * known-int.h - prototypes utiles aux formats binaires reconnus * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,7 +26,7 @@ #include "known.h" -#include "../analysis/storage/storage.h" +//#include "../analysis/storage/storage.h" @@ -37,16 +37,15 @@ typedef char * (* known_get_key_fc) (const GKnownFormat *); typedef char * (* known_get_desc_fc) (const GKnownFormat *); /*Assure l'interprétation d'un format en différé. */ -typedef bool (* known_analyze_fc) (GKnownFormat *, wgroup_id_t, GtkStatusStack *); - -/* Réalise un traitement post-désassemblage. */ -typedef void (* known_complete_analysis_fc) (GKnownFormat *, wgroup_id_t, GtkStatusStack *); +typedef bool (* known_analyze_fc) (GKnownFormat *); +#if 0 /* Charge un format depuis une mémoire tampon. */ typedef bool (* load_known_fc) (GKnownFormat *, GObjectStorage *, packed_buffer_t *); /* Sauvegarde un format dans une mémoire tampon. */ typedef bool (* store_known_fc) (GKnownFormat *, GObjectStorage *, packed_buffer_t *); +#endif /* Format binaire générique (instance) */ @@ -67,13 +66,18 @@ struct _GKnownFormatClass known_get_desc_fc get_desc; /* Désignation humaine */ known_analyze_fc analyze; /* Interprétation du format */ - known_complete_analysis_fc complete; /* Terminaison d'analyse */ +#if 0 load_known_fc load; /* Chargement depuis un tampon */ store_known_fc store; /* Conservation dans un tampon */ +#endif }; +/* Met en place un nouveau contenu binaire à analyser. */ +bool g_known_format_create(GKnownFormat *, GBinContent *); + + #endif /* _FORMAT_KNOWN_INT_H */ diff --git a/src/format/known.c b/src/format/known.c index 7512ae1..bc03793 100644 --- a/src/format/known.c +++ b/src/format/known.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * format.c - support des différents formats binaires reconnus * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,8 +28,8 @@ #include "known-int.h" -#include "../analysis/storage/serialize-int.h" -#include "../plugins/pglist.h" +//#include "../analysis/storage/serialize-int.h" +//#include "../plugins/pglist.h" @@ -43,7 +43,7 @@ static void g_known_format_class_init(GKnownFormatClass *); static void g_known_format_init(GKnownFormat *); /* Procède à l'initialisation de l'interface de sérialisation. */ -static void g_known_format_serializable_init(GSerializableObjectInterface *); +//static void g_known_format_serializable_init(GSerializableObjectInterface *); /* Supprime toutes les références externes. */ static void g_known_format_dispose(GKnownFormat *); @@ -56,6 +56,7 @@ static void g_known_format_finalize(GKnownFormat *); /* -------------------- CONSERVATION ET RECHARGEMENT DES DONNEES -------------------- */ +#if 0 /* Charge un format depuis une mémoire tampon. */ static bool _g_known_format_load(GKnownFormat *, GObjectStorage *, packed_buffer_t *); @@ -67,7 +68,7 @@ static bool _g_known_format_store(GKnownFormat *, GObjectStorage *, packed_buffe /* Sauvegarde un format dans une mémoire tampon. */ static bool g_known_format_store(GKnownFormat *, GObjectStorage *, packed_buffer_t *); - +#endif /* ---------------------------------------------------------------------------------- */ @@ -76,8 +77,9 @@ static bool g_known_format_store(GKnownFormat *, GObjectStorage *, packed_buffer /* Indique le type défini pour un format binaire générique. */ -G_DEFINE_TYPE_WITH_CODE(GKnownFormat, g_known_format, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_known_format_serializable_init)); +//G_DEFINE_TYPE_WITH_CODE(GKnownFormat, g_known_format, G_TYPE_OBJECT, +// G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_known_format_serializable_init)); +G_DEFINE_TYPE(GKnownFormat, g_known_format, G_TYPE_OBJECT); /****************************************************************************** @@ -101,8 +103,10 @@ static void g_known_format_class_init(GKnownFormatClass *klass) object->dispose = (GObjectFinalizeFunc/* ! */)g_known_format_dispose; object->finalize = (GObjectFinalizeFunc)g_known_format_finalize; + /* klass->load = (load_known_fc)_g_known_format_load; klass->store = (store_known_fc)_g_known_format_store; + */ } @@ -126,6 +130,7 @@ static void g_known_format_init(GKnownFormat *format) } +#if 0 /****************************************************************************** * * * Paramètres : iface = interface GLib à initialiser. * @@ -144,6 +149,7 @@ static void g_known_format_serializable_init(GSerializableObjectInterface *iface iface->store = (store_serializable_object_cb)g_known_format_store; } +#endif /****************************************************************************** @@ -188,24 +194,27 @@ static void g_known_format_finalize(GKnownFormat *format) /****************************************************************************** * * -* Paramètres : format = description du format connu à consulter. * +* Paramètres : format = description du format à initialiser pleinement. * * content = contenu binaire à parcourir. * * * -* Description : Définit le contenu binaire à analyser. * +* Description : Met en place un nouveau contenu binaire à analyser. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -void g_known_format_set_content(GKnownFormat *format, GBinContent *content) +bool g_known_format_create(GKnownFormat *format, GBinContent *content) { - assert(format->content == NULL); + bool result; /* Bilan à retourner */ - g_object_ref_sink(G_OBJECT(content)); + result = true; format->content = content; + ref_object(content); + + return result; } @@ -251,8 +260,13 @@ GBinContent *g_known_format_get_content(const GKnownFormat *format) char *g_known_format_get_key(const GKnownFormat *format) { char *result; /* Désignation à retourner */ + GKnownFormatClass *class; /* Classe de l'instance */ + + class = G_KNOWN_FORMAT_GET_CLASS(format); - result = G_KNOWN_FORMAT_GET_CLASS(format)->get_key(format); + result = class->get_key(format); + + //assert(result != NULL); return result; @@ -274,8 +288,13 @@ char *g_known_format_get_key(const GKnownFormat *format) char *g_known_format_get_description(const GKnownFormat *format) { char *result; /* Description à retourner */ + GKnownFormatClass *class; /* Classe de l'instance */ + + class = G_KNOWN_FORMAT_GET_CLASS(format); + + result = class->get_desc(format); - result = G_KNOWN_FORMAT_GET_CLASS(format)->get_desc(format); + //assert(result != NULL); return result; @@ -285,8 +304,6 @@ char *g_known_format_get_description(const GKnownFormat *format) /****************************************************************************** * * * Paramètres : format = format chargé dont l'analyse est lancée. * -* gid = groupe de travail dédié. * -* status = barre de statut à tenir informée. * * * * Description : Assure l'interprétation d'un format en différé. * * * @@ -296,60 +313,30 @@ char *g_known_format_get_description(const GKnownFormat *format) * * ******************************************************************************/ -bool g_known_format_analyze(GKnownFormat *format, wgroup_id_t gid, GtkStatusStack *status) +bool g_known_format_analyze(GKnownFormat *format) { bool result; /* Bilan à retourner */ GKnownFormatClass *class; /* Classe de l'instance */ - handle_known_format_analysis(PGA_FORMAT_ANALYSIS_STARTED, format, gid, status); + //handle_known_format_analysis(PGA_FORMAT_ANALYSIS_STARTED, format, gid, status); class = G_KNOWN_FORMAT_GET_CLASS(format); - result = class->analyze(format, gid, status); + result = class->analyze(format); - handle_known_format_analysis(PGA_FORMAT_ANALYSIS_ENDED, format, gid, status); + //handle_known_format_analysis(PGA_FORMAT_ANALYSIS_ENDED, format, gid, status); return result; } -/****************************************************************************** -* * -* Paramètres : format = description de l'exécutable à manipuler. * -* gid = groupe de travail dédié. * -* status = barre de statut à tenir informée. * -* * -* Description : Réalise un traitement post-désassemblage. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_known_format_complete_analysis(GKnownFormat *format, wgroup_id_t gid, GtkStatusStack *status) -{ - GKnownFormatClass *class; /* Classe de l'instance */ - - handle_known_format_analysis(PGA_FORMAT_POST_ANALYSIS_STARTED, format, gid, status); - - class = G_KNOWN_FORMAT_GET_CLASS(format); - - if (class->complete != NULL) - class->complete(format, gid, status); - - handle_known_format_analysis(PGA_FORMAT_POST_ANALYSIS_ENDED, format, gid, status); - -} - - /* ---------------------------------------------------------------------------------- */ /* CONSERVATION ET RECHARGEMENT DES DONNEES */ /* ---------------------------------------------------------------------------------- */ - +#if 0 /****************************************************************************** * * * Paramètres : operand = élément GLib à constuire. * @@ -468,3 +455,4 @@ static bool g_known_format_store(GKnownFormat *format, GObjectStorage *storage, return result; } +#endif diff --git a/src/format/known.h b/src/format/known.h index dcc8669..41236df 100644 --- a/src/format/known.h +++ b/src/format/known.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * format.h - prototypes pour le support des différents formats binaires reconnus * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,37 +25,17 @@ #define _FORMAT_KNOWN_H -#include <glib-object.h> -#include <stdbool.h> - - #include "../analysis/content.h" -#include "../glibext/delayed.h" -#include "../glibext/notifier.h" - +#include "../glibext/helpers.h" -#define G_TYPE_KNOWN_FORMAT g_known_format_get_type() -#define G_KNOWN_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KNOWN_FORMAT, GKnownFormat)) -#define G_IS_KNOWN_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KNOWN_FORMAT)) -#define G_KNOWN_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KNOWN_FORMAT, GKnownFormatClass)) -#define G_IS_KNOWN_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KNOWN_FORMAT)) -#define G_KNOWN_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KNOWN_FORMAT, GKnownFormatClass)) +#define G_TYPE_KNOWN_FORMAT (g_known_format_get_type()) -/* Format binaire générique (instance) */ -typedef struct _GKnownFormat GKnownFormat; +DECLARE_GTYPE(GKnownFormat, g_known_format, G, KNOWN_FORMAT); -/* Format binaire générique (classe) */ -typedef struct _GKnownFormatClass GKnownFormatClass; -/* Indique le type défini pour un format binaire générique. */ -GType g_known_format_get_type(void); - -/* Définit le contenu binaire à analyser. */ -void g_known_format_set_content(GKnownFormat *, GBinContent *); - /* Fournit une référence vers le contenu binaire analysé. */ GBinContent *g_known_format_get_content(const GKnownFormat *); @@ -66,10 +46,7 @@ char *g_known_format_get_key(const GKnownFormat *); char *g_known_format_get_description(const GKnownFormat *); /* Assure l'interprétation d'un format en différé. */ -bool g_known_format_analyze(GKnownFormat *, wgroup_id_t, GtkStatusStack *); - -/* Réalise un traitement post-désassemblage. */ -void g_known_format_complete_analysis(GKnownFormat *, wgroup_id_t, GtkStatusStack *); +bool g_known_format_analyze(GKnownFormat *); diff --git a/src/format/format-int.h b/src/format/program-int.h index f18bb24..8464fe4 100644 --- a/src/format/format-int.h +++ b/src/format/program-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * format-int.h - prototypes utiles aux formats binaires + * program-int.h - prototypes internes pour le support des différents formats de programmes * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,14 +21,59 @@ */ -#ifndef _FORMAT_FORMAT_INT_H -#define _FORMAT_FORMAT_INT_H +#ifndef _FORMAT_PROGRAM_INT_H +#define _FORMAT_PROGRAM_INT_H -#include "format.h" +#include "program.h" #include "known-int.h" + + + +/* Indique le boutisme employé par le format binaire analysé. */ +typedef SourceEndian (* program_get_endian_fc) (const GProgramFormat *); + +/* Fournit l'emplacement d'une section donnée. */ +typedef bool (* find_range_by_name_fc) (const GProgramFormat *, const char *, mrange_t *); + + +/* Format de programme générique (instance) */ +struct _GProgramFormat +{ + GKnownFormat parent; /* A laisser en premier */ + +}; + +/* Format de programme générique (classe) */ +struct _GProgramFormatClass +{ + GKnownFormatClass parent; /* A laisser en premier */ + + program_get_endian_fc get_endian; /* Boutisme employé */ + find_range_by_name_fc find_range_by_name; /* Emplacement de sections */ + +}; + + +/* Met en place un nouveau contenu binaire à analyser. */ +bool g_program_format_create(GProgramFormat *, GBinContent *); + + + + + + + + + + + + + +#if 0 + #include "preload.h" #include "../glibext/objhole.h" #include "../mangling/demangler.h" @@ -38,9 +83,6 @@ /* ------------------------ TRAITEMENT INDIVIDUEL DE FORMATS ------------------------ */ -/* Indique le boutisme employé par le format binaire analysé. */ -typedef SourceEndian (* format_get_endian_fc) (const GBinFormat *); - /* Rythme des allocations pour les entrées de code */ #define EXTRA_POINT_BLOCK 20 @@ -120,8 +162,6 @@ struct _GBinFormatClass { GKnownFormatClass parent; /* A laisser en premier */ - format_get_endian_fc get_endian; /* Boutisme employé */ - /* Signaux */ void (* symbol_added) (GBinFormat *, GBinSymbol *); @@ -156,5 +196,8 @@ GDataType *g_binary_format_decode_type(const GBinFormat *, const char *); GBinRoutine *g_binary_format_decode_routine(const GBinFormat *, const char *); +#endif + + -#endif /* _FORMAT_FORMAT_INT_H */ +#endif /* _FORMAT_PROGRAM_INT_H */ diff --git a/src/format/format.c b/src/format/program.c index d126236..9b9df81 100644 --- a/src/format/format.c +++ b/src/format/program.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * format.c - support des différents formats binaires + * program.c - support des différents formats de programmes * - * Copyright (C) 2009-2020 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,40 +21,45 @@ */ -#include "format.h" +#include "program.h" -#include <assert.h> -#include <malloc.h> -#include <string.h> +//#include <assert.h> +//#include <malloc.h> +//#include <string.h> -#include "format-int.h" +#include "program-int.h" + +/* #include "preload.h" #include "../arch/processor.h" #include "../common/sort.h" #include "../core/demanglers.h" #include "../plugins/pglist.h" +*/ /* Initialise la classe des formats binaires génériques. */ -static void g_binary_format_class_init(GBinFormatClass *); +static void g_program_format_class_init(GProgramFormatClass *); /* Initialise une instance de format binaire générique. */ -static void g_binary_format_init(GBinFormat *); +static void g_program_format_init(GProgramFormat *); /* Supprime toutes les références externes. */ -static void g_binary_format_dispose(GBinFormat *); +static void g_program_format_dispose(GProgramFormat *); /* Procède à la libération totale de la mémoire. */ -static void g_binary_format_finalize(GBinFormat *); +static void g_program_format_finalize(GProgramFormat *); + +#if 0 /* Charge les plages de couvertures depuis une mémoire tampon. */ -static bool g_binary_format_load_start_points(GBinFormat *, packed_buffer_t *); +static bool g_program_format_load_start_points(GProgramFormat *, packed_buffer_t *); /* Sauvegarde les points de départ enregistrés pour un format. */ -static bool g_binary_format_store_start_points(GBinFormat *, packed_buffer_t *); +static bool g_program_format_store_start_points(GProgramFormat *, packed_buffer_t *); @@ -62,13 +67,13 @@ static bool g_binary_format_store_start_points(GBinFormat *, packed_buffer_t *); /* Retire un symbole de la collection du format binaire. */ -static void _g_binary_format_remove_symbol(GBinFormat *, size_t); +static void _g_program_format_remove_symbol(GProgramFormat *, size_t); /* Recherche le symbole associé à une adresse. */ -static bool _g_binary_format_find_symbol(const GBinFormat *, const vmpa2t *, __compar_fn_t, size_t *, GBinSymbol **); +static bool _g_program_format_find_symbol(const GProgramFormat *, const vmpa2t *, __compar_fn_t, size_t *, GBinSymbol **); /* Recherche un symbole particulier. */ -static bool __g_binary_format_find_symbol(const GBinFormat *, const void *, __compar_fn_t, size_t *, GBinSymbol **); +static bool __g_program_format_find_symbol(const GProgramFormat *, const void *, __compar_fn_t, size_t *, GBinSymbol **); @@ -76,10 +81,10 @@ static bool __g_binary_format_find_symbol(const GBinFormat *, const void *, __co /* Charge les erreurs de chargement depuis une mémoire tampon. */ -static bool g_binary_format_load_errors(GBinFormat *, packed_buffer_t *); +static bool g_program_format_load_errors(GProgramFormat *, packed_buffer_t *); /* Sauvegarde les erreurs de chargement dans une mémoire tampon. */ -static bool g_binary_format_store_errors(GBinFormat *, packed_buffer_t *); +static bool g_program_format_store_errors(GProgramFormat *, packed_buffer_t *); @@ -87,17 +92,18 @@ static bool g_binary_format_store_errors(GBinFormat *, packed_buffer_t *); /* Charge un format depuis une mémoire tampon. */ -static bool g_binary_format_load(GBinFormat *, GObjectStorage *, packed_buffer_t *); +static bool g_program_format_load(GProgramFormat *, GObjectStorage *, packed_buffer_t *); /* Sauvegarde un format dans une mémoire tampon. */ -static bool g_binary_format_store(GBinFormat *, GObjectStorage *, packed_buffer_t *); +static bool g_program_format_store(GProgramFormat *, GObjectStorage *, packed_buffer_t *); +#endif /* Indique le type défini pour un format binaire générique. */ -G_DEFINE_TYPE(GBinFormat, g_binary_format, G_TYPE_KNOWN_FORMAT); +G_DEFINE_TYPE(GProgramFormat, g_program_format, G_TYPE_KNOWN_FORMAT); /****************************************************************************** @@ -112,25 +118,27 @@ G_DEFINE_TYPE(GBinFormat, g_binary_format, G_TYPE_KNOWN_FORMAT); * * ******************************************************************************/ -static void g_binary_format_class_init(GBinFormatClass *klass) +static void g_program_format_class_init(GProgramFormatClass *klass) { GObjectClass *object; /* Autre version de la classe */ - GKnownFormatClass *known; /* Version de classe parente */ + //GKnownFormatClass *known; /* Version de classe parente */ object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_format_dispose; - object->finalize = (GObjectFinalizeFunc)g_binary_format_finalize; + object->dispose = (GObjectFinalizeFunc/* ! */)g_program_format_dispose; + object->finalize = (GObjectFinalizeFunc)g_program_format_finalize; + +#if 0 known = G_KNOWN_FORMAT_CLASS(klass); - known->load = (load_known_fc)g_binary_format_load; - known->store = (load_known_fc)g_binary_format_store; + known->load = (load_known_fc)g_program_format_load; + known->store = (load_known_fc)g_program_format_store; g_signal_new("symbol-added", G_TYPE_BIN_FORMAT, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GBinFormatClass, symbol_added), + G_STRUCT_OFFSET(GProgramFormatClass, symbol_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); @@ -138,11 +146,13 @@ static void g_binary_format_class_init(GBinFormatClass *klass) g_signal_new("symbol-removed", G_TYPE_BIN_FORMAT, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GBinFormatClass, symbol_removed), + G_STRUCT_OFFSET(GProgramFormatClass, symbol_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); +#endif + } @@ -158,8 +168,10 @@ static void g_binary_format_class_init(GBinFormatClass *klass) * * ******************************************************************************/ -static void g_binary_format_init(GBinFormat *format) +static void g_program_format_init(GProgramFormat *format) { + +#if 0 fmt_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_BIN_FORMAT_EXTRA(format); @@ -184,6 +196,8 @@ static void g_binary_format_init(GBinFormat *format) g_atomic_int_set(&format->error_locked, 0); #endif +#endif + } @@ -199,8 +213,10 @@ static void g_binary_format_init(GBinFormat *format) * * ******************************************************************************/ -static void g_binary_format_dispose(GBinFormat *format) +static void g_program_format_dispose(GProgramFormat *format) { +#if 0 + size_t i; /* Boucle de parcours */ g_rw_lock_clear(&format->pt_lock); @@ -216,7 +232,10 @@ static void g_binary_format_dispose(GBinFormat *format) g_mutex_clear(&format->error_mutex); - G_OBJECT_CLASS(g_binary_format_parent_class)->dispose(G_OBJECT(format)); +#endif + + + G_OBJECT_CLASS(g_program_format_parent_class)->dispose(G_OBJECT(format)); } @@ -233,8 +252,10 @@ static void g_binary_format_dispose(GBinFormat *format) * * ******************************************************************************/ -static void g_binary_format_finalize(GBinFormat *format) +static void g_program_format_finalize(GProgramFormat *format) { +#if 0 + DisassPriorityLevel i; /* Boucle de parcours #1 */ size_t k; /* Boucle de parcours #2 */ @@ -255,13 +276,43 @@ static void g_binary_format_finalize(GBinFormat *format) } - G_OBJECT_CLASS(g_binary_format_parent_class)->finalize(G_OBJECT(format)); +#endif + + + + G_OBJECT_CLASS(g_program_format_parent_class)->finalize(G_OBJECT(format)); } /****************************************************************************** * * +* Paramètres : format = description du format à initialiser pleinement. * +* content = contenu binaire à parcourir. * +* * +* Description : Met en place un nouveau contenu binaire à analyser. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_program_format_create(GProgramFormat *format, GBinContent *content) +{ + bool result; /* Bilan à retourner */ + + result = g_known_format_create(G_KNOWN_FORMAT(format), content); + + return result; + +} + + +#if 0 + +/****************************************************************************** +* * * Paramètres : format = format à venir modifier. * * flag = drapeau d'information complémentaire à planter. * * * @@ -273,7 +324,7 @@ static void g_binary_format_finalize(GBinFormat *format) * * ******************************************************************************/ -bool g_binary_format_set_flag(GBinFormat *format, FormatFlag flag) +bool g_program_format_set_flag(GProgramFormat *format, FormatFlag flag) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à modifier */ @@ -306,7 +357,7 @@ bool g_binary_format_set_flag(GBinFormat *format, FormatFlag flag) * * ******************************************************************************/ -bool g_binary_format_unset_flag(GBinFormat *format, FormatFlag flag) +bool g_program_format_unset_flag(GProgramFormat *format, FormatFlag flag) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à modifier */ @@ -339,7 +390,7 @@ bool g_binary_format_unset_flag(GBinFormat *format, FormatFlag flag) * * ******************************************************************************/ -bool g_binary_format_has_flag(const GBinFormat *format, FormatFlag flag) +bool g_program_format_has_flag(const GProgramFormat *format, FormatFlag flag) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à modifier */ @@ -369,7 +420,7 @@ bool g_binary_format_has_flag(const GBinFormat *format, FormatFlag flag) * * ******************************************************************************/ -FormatFlag g_binary_format_get_flags(const GBinFormat *format) +FormatFlag g_program_format_get_flags(const GProgramFormat *format) { FormatFlag result; /* Fanions à retourner */ fmt_extra_data_t *extra; /* Données insérées à modifier */ @@ -386,6 +437,8 @@ FormatFlag g_binary_format_get_flags(const GBinFormat *format) } +#endif + /****************************************************************************** * * @@ -399,11 +452,14 @@ FormatFlag g_binary_format_get_flags(const GBinFormat *format) * * ******************************************************************************/ -SourceEndian g_binary_format_get_endianness(const GBinFormat *format) +SourceEndian g_program_format_get_endianness(const GProgramFormat *format) { SourceEndian result; /* Boutisme à retourner */ + GProgramFormatClass *class; /* Classe de l'instance */ + + class = G_PROGRAM_FORMAT_GET_CLASS(format); - result = G_BIN_FORMAT_GET_CLASS(format)->get_endian(format); + result = class->get_endian(format); return result; @@ -412,6 +468,40 @@ SourceEndian g_binary_format_get_endianness(const GBinFormat *format) /****************************************************************************** * * +* Paramètres : format = description du programme à consulter. * +* name = nom de la section recherchée. * +* range = emplacement en mémoire à renseigner. [OUT] * +* * +* Description : Fournit l'emplacement d'une section donnée. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_program_format_find_section_range_by_name(const GProgramFormat *format, const char *name, mrange_t *range) +{ + bool result; /* Bilan à retourner */ + GProgramFormatClass *class; /* Classe de l'instance */ + + class = G_PROGRAM_FORMAT_GET_CLASS(format); + + if (class->find_range_by_name == NULL) + result = false; + + else + result = class->find_range_by_name(format, name, range); + + return result; + +} + + +#if 0 + +/****************************************************************************** +* * * Paramètres : format = description de l'exécutable à compléter. * * pt = point de l'espace mémoire à considérer. * * level = indication de priorité et d'origine de l'adresse. * @@ -424,7 +514,7 @@ SourceEndian g_binary_format_get_endianness(const GBinFormat *format) * * ******************************************************************************/ -void g_binary_format_register_code_point(GBinFormat *format, virt_t pt, DisassPriorityLevel level) +void g_program_format_register_code_point(GProgramFormat *format, virt_t pt, DisassPriorityLevel level) { assert(level < DPL_COUNT); @@ -459,7 +549,7 @@ void g_binary_format_register_code_point(GBinFormat *format, virt_t pt, DisassPr * * ******************************************************************************/ -static bool g_binary_format_load_start_points(GBinFormat *format, packed_buffer_t *pbuf) +static bool g_program_format_load_start_points(GProgramFormat *format, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ DisassPriorityLevel i; /* Boucle de parcours #1 */ @@ -512,7 +602,7 @@ static bool g_binary_format_load_start_points(GBinFormat *format, packed_buffer_ * * ******************************************************************************/ -static bool g_binary_format_store_start_points(GBinFormat *format, packed_buffer_t *pbuf) +static bool g_program_format_store_start_points(GProgramFormat *format, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ DisassPriorityLevel i; /* Boucle de parcours #1 */ @@ -555,7 +645,7 @@ static bool g_binary_format_store_start_points(GBinFormat *format, packed_buffer * * ******************************************************************************/ -void g_binary_format_preload_disassembling_context(GBinFormat *format, GProcContext *ctx, GtkStatusStack *status) +void g_program_format_preload_disassembling_context(GProgramFormat *format, GProcContext *ctx, GtkStatusStack *status) { g_preload_info_copy(format->info, G_PRELOAD_INFO(ctx)); @@ -576,7 +666,7 @@ void g_binary_format_preload_disassembling_context(GBinFormat *format, GProcCont * * ******************************************************************************/ -void g_binary_format_activate_disassembling_context(GBinFormat *format, GProcContext *ctx, GtkStatusStack *status) +void g_program_format_activate_disassembling_context(GProgramFormat *format, GProcContext *ctx, GtkStatusStack *status) { DisassPriorityLevel i; /* Boucle de parcours #1 */ size_t k; /* Boucle de parcours #2 */ @@ -610,7 +700,7 @@ void g_binary_format_activate_disassembling_context(GBinFormat *format, GProcCon * * ******************************************************************************/ -GCompDemangler *g_binary_format_get_demangler(const GBinFormat *format) +GCompDemangler *g_program_format_get_demangler(const GProgramFormat *format) { GCompDemangler *result; /* Décodeur à retourner */ @@ -637,7 +727,7 @@ GCompDemangler *g_binary_format_get_demangler(const GBinFormat *format) * * ******************************************************************************/ -GDataType *g_binary_format_decode_type(const GBinFormat *format, const char *desc) +GDataType *g_program_format_decode_type(const GProgramFormat *format, const char *desc) { GDataType *result; /* Construction à remonter */ GCompDemangler *demangler; /* Accès plus lisible */ @@ -667,7 +757,7 @@ GDataType *g_binary_format_decode_type(const GBinFormat *format, const char *des * * ******************************************************************************/ -GBinRoutine *g_binary_format_decode_routine(const GBinFormat *format, const char *desc) +GBinRoutine *g_program_format_decode_routine(const GProgramFormat *format, const char *desc) { GBinRoutine *result; /* Construction à remonter */ GCompDemangler *demangler; /* Accès plus lisible */ @@ -708,7 +798,7 @@ GBinRoutine *g_binary_format_decode_routine(const GBinFormat *format, const char * * ******************************************************************************/ -void g_binary_format_lock_unlock_symbols_rd(GBinFormat *format, bool state) +void g_program_format_lock_unlock_symbols_rd(GProgramFormat *format, bool state) { #ifndef NDEBUG gint test; /* Test de valeur courante */ @@ -746,7 +836,7 @@ void g_binary_format_lock_unlock_symbols_rd(GBinFormat *format, bool state) * * ******************************************************************************/ -void g_binary_format_lock_unlock_symbols_wr(GBinFormat *format, bool state) +void g_program_format_lock_unlock_symbols_wr(GProgramFormat *format, bool state) { if (state) { @@ -778,7 +868,7 @@ void g_binary_format_lock_unlock_symbols_wr(GBinFormat *format, bool state) * * ******************************************************************************/ #ifndef NDEBUG -void g_binary_format_check_for_symbols_lock(const GBinFormat *format) +void g_program_format_check_for_symbols_lock(const GProgramFormat *format) { assert(g_atomic_int_get(&format->sym_locked) > 0); @@ -798,7 +888,7 @@ void g_binary_format_check_for_symbols_lock(const GBinFormat *format) * * ******************************************************************************/ -unsigned int g_binary_format_get_symbols_stamp(const GBinFormat *format) +unsigned int g_program_format_get_symbols_stamp(const GProgramFormat *format) { return format->sym_stamp; @@ -817,7 +907,7 @@ unsigned int g_binary_format_get_symbols_stamp(const GBinFormat *format) * * ******************************************************************************/ -size_t g_binary_format_count_symbols(const GBinFormat *format) +size_t g_program_format_count_symbols(const GProgramFormat *format) { assert(g_atomic_int_get(&format->sym_locked) > 0); @@ -839,7 +929,7 @@ size_t g_binary_format_count_symbols(const GBinFormat *format) * * ******************************************************************************/ -GBinSymbol *g_binary_format_get_symbol(const GBinFormat *format, size_t index) +GBinSymbol *g_program_format_get_symbol(const GProgramFormat *format, size_t index) { GBinSymbol *result; /* Symbole à retourner */ @@ -877,7 +967,7 @@ GBinSymbol *g_binary_format_get_symbol(const GBinFormat *format, size_t index) * * ******************************************************************************/ -bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol) +bool g_program_format_add_symbol(GProgramFormat *format, GBinSymbol *symbol) { bool result; /* Statut d'ajout à retourner */ #ifndef NDEBUG @@ -887,7 +977,7 @@ bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol) size_t index; /* Indice du point d'insertion */ /** - * Pour que les fonctions de recherche basées sur _g_binary_format_find_symbol() + * Pour que les fonctions de recherche basées sur _g_program_format_find_symbol() * fassent bien leur office, il faut que les symboles soient triés. * * Cependant, les localisations à satisfaire lors d'une recherche recontrent @@ -912,7 +1002,7 @@ bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol) assert(has_phys_addr(addr) || g_binary_symbol_get_status(symbol) == SSS_DYNAMIC); #endif - g_binary_format_lock_unlock_symbols_wr(format, true); + g_program_format_lock_unlock_symbols_wr(format, true); /** * Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution @@ -939,7 +1029,7 @@ bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol) else g_object_unref(G_OBJECT(symbol)); - g_binary_format_lock_unlock_symbols_wr(format, false); + g_program_format_lock_unlock_symbols_wr(format, false); if (result) g_signal_emit_by_name(format, "symbol-added", symbol); @@ -963,7 +1053,7 @@ bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol) * * ******************************************************************************/ -bool g_binary_format_add_symbols(GBinFormat *format, GBinSymbol **symbols, size_t count) +bool g_program_format_add_symbols(GProgramFormat *format, GBinSymbol **symbols, size_t count) { bool result; /* Statut d'ajout à retourner */ #ifndef NDEBUG @@ -977,7 +1067,7 @@ bool g_binary_format_add_symbols(GBinFormat *format, GBinSymbol **symbols, size_ size_t index; /* Indice du point d'insertion */ /** - * Pour que les fonctions de recherche basées sur _g_binary_format_find_symbol() + * Pour que les fonctions de recherche basées sur _g_program_format_find_symbol() * fassent bien leur office, il faut que les symboles soient triés. * * Cependant, les localisations à satisfaire lors d'une recherche recontrent @@ -1014,7 +1104,7 @@ bool g_binary_format_add_symbols(GBinFormat *format, GBinSymbol **symbols, size_ } #endif - g_binary_format_lock_unlock_symbols_wr(format, true); + g_program_format_lock_unlock_symbols_wr(format, true); /** * Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution @@ -1042,7 +1132,7 @@ bool g_binary_format_add_symbols(GBinFormat *format, GBinSymbol **symbols, size_ } - g_binary_format_lock_unlock_symbols_wr(format, false); + g_program_format_lock_unlock_symbols_wr(format, false); if (result) for (i = 0; i < count; i++) @@ -1066,7 +1156,7 @@ bool g_binary_format_add_symbols(GBinFormat *format, GBinSymbol **symbols, size_ * * ******************************************************************************/ -static void _g_binary_format_remove_symbol(GBinFormat *format, size_t index) +static void _g_program_format_remove_symbol(GProgramFormat *format, size_t index) { assert(g_atomic_int_get(&format->sym_locked) == 1); @@ -1096,22 +1186,22 @@ static void _g_binary_format_remove_symbol(GBinFormat *format, size_t index) * * ******************************************************************************/ -void g_binary_format_remove_symbol(GBinFormat *format, GBinSymbol *symbol) +void g_program_format_remove_symbol(GProgramFormat *format, GBinSymbol *symbol) { bool found; /* Jeton de présence */ size_t index; /* Indice du point de retrait */ g_object_ref(G_OBJECT(symbol)); - g_binary_format_lock_unlock_symbols_wr(format, true); + g_program_format_lock_unlock_symbols_wr(format, true); found = bsearch_index(&symbol, format->symbols, format->sym_count, sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index); if (found) - _g_binary_format_remove_symbol(format, index); + _g_program_format_remove_symbol(format, index); - g_binary_format_lock_unlock_symbols_wr(format, false); + g_program_format_lock_unlock_symbols_wr(format, false); if (found) g_signal_emit_by_name(format, "symbol-removed", symbol); @@ -1135,7 +1225,7 @@ void g_binary_format_remove_symbol(GBinFormat *format, GBinSymbol *symbol) * * ******************************************************************************/ -bool g_binary_format_find_symbol_by_label(GBinFormat *format, const char *label, GBinSymbol **symbol) +bool g_program_format_find_symbol_by_label(GProgramFormat *format, const char *label, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ @@ -1143,7 +1233,7 @@ bool g_binary_format_find_symbol_by_label(GBinFormat *format, const char *label, result = false; - g_binary_format_lock_symbols_rd(format); + g_program_format_lock_symbols_rd(format); for (i = 0; i < format->sym_count && !result; i++) { @@ -1163,7 +1253,7 @@ bool g_binary_format_find_symbol_by_label(GBinFormat *format, const char *label, } - g_binary_format_unlock_symbols_rd(format); + g_program_format_unlock_symbols_rd(format); return result; @@ -1186,16 +1276,16 @@ bool g_binary_format_find_symbol_by_label(GBinFormat *format, const char *label, * * ******************************************************************************/ -static bool _g_binary_format_find_symbol(const GBinFormat *format, const vmpa2t *addr, __compar_fn_t fn, size_t *index, GBinSymbol **symbol) +static bool _g_program_format_find_symbol(const GProgramFormat *format, const vmpa2t *addr, __compar_fn_t fn, size_t *index, GBinSymbol **symbol) { /** * Pour ce qui est des justifications quant à la vérification suivante, - * se référer aux commentaires placés dans g_binary_format_add_symbol(). + * se référer aux commentaires placés dans g_program_format_add_symbol(). */ assert(has_phys_addr(addr)); - return __g_binary_format_find_symbol(format, addr, fn, index, symbol); + return __g_program_format_find_symbol(format, addr, fn, index, symbol); } @@ -1216,7 +1306,7 @@ static bool _g_binary_format_find_symbol(const GBinFormat *format, const vmpa2t * * ******************************************************************************/ -static bool __g_binary_format_find_symbol(const GBinFormat *format, const void *key, __compar_fn_t fn, size_t *index, GBinSymbol **symbol) +static bool __g_program_format_find_symbol(const GProgramFormat *format, const void *key, __compar_fn_t fn, size_t *index, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ void *found; /* Résultat de recherches */ @@ -1268,7 +1358,7 @@ static bool __g_binary_format_find_symbol(const GBinFormat *format, const void * * * ******************************************************************************/ -bool g_binary_format_find_symbol_index_at(GBinFormat *format, const vmpa2t *addr, size_t *index) +bool g_program_format_find_symbol_index_at(GProgramFormat *format, const vmpa2t *addr, size_t *index) { bool result; /* Bilan à retourner */ @@ -1282,11 +1372,11 @@ bool g_binary_format_find_symbol_index_at(GBinFormat *format, const vmpa2t *addr } - g_binary_format_lock_symbols_rd(format); + g_program_format_lock_symbols_rd(format); - result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, index, NULL); + result = _g_program_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, index, NULL); - g_binary_format_unlock_symbols_rd(format); + g_program_format_unlock_symbols_rd(format); return result; @@ -1307,7 +1397,7 @@ bool g_binary_format_find_symbol_index_at(GBinFormat *format, const vmpa2t *addr * * ******************************************************************************/ -bool g_binary_format_find_symbol_at(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol) +bool g_program_format_find_symbol_at(GProgramFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ @@ -1321,11 +1411,11 @@ bool g_binary_format_find_symbol_at(GBinFormat *format, const vmpa2t *addr, GBin } - g_binary_format_lock_symbols_rd(format); + g_program_format_lock_symbols_rd(format); - result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol); + result = _g_program_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol); - g_binary_format_unlock_symbols_rd(format); + g_program_format_unlock_symbols_rd(format); return result; @@ -1346,7 +1436,7 @@ bool g_binary_format_find_symbol_at(GBinFormat *format, const vmpa2t *addr, GBin * * ******************************************************************************/ -bool g_binary_format_find_symbol_for(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol) +bool g_program_format_find_symbol_for(GProgramFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ @@ -1360,11 +1450,11 @@ bool g_binary_format_find_symbol_for(GBinFormat *format, const vmpa2t *addr, GBi } - g_binary_format_lock_symbols_rd(format); + g_program_format_lock_symbols_rd(format); - result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol); + result = _g_program_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol); - g_binary_format_unlock_symbols_rd(format); + g_program_format_unlock_symbols_rd(format); return result; @@ -1385,7 +1475,7 @@ bool g_binary_format_find_symbol_for(GBinFormat *format, const vmpa2t *addr, GBi * * ******************************************************************************/ -bool g_binary_format_find_next_symbol_at(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol) +bool g_program_format_find_next_symbol_at(GProgramFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ size_t index; /* Indice à considérer */ @@ -1400,9 +1490,9 @@ bool g_binary_format_find_next_symbol_at(GBinFormat *format, const vmpa2t *addr, } - g_binary_format_lock_symbols_rd(format); + g_program_format_lock_symbols_rd(format); - result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, &index, NULL); + result = _g_program_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, &index, NULL); if (result && (index + 1) < format->sym_count) { @@ -1417,7 +1507,7 @@ bool g_binary_format_find_next_symbol_at(GBinFormat *format, const vmpa2t *addr, result = false; } - g_binary_format_unlock_symbols_rd(format); + g_program_format_unlock_symbols_rd(format); return result; @@ -1438,7 +1528,7 @@ bool g_binary_format_find_next_symbol_at(GBinFormat *format, const vmpa2t *addr, * * ******************************************************************************/ -bool g_binary_format_find_first_symbol_inside(GBinFormat *format, const mrange_t *range, size_t *index) +bool g_program_format_find_first_symbol_inside(GProgramFormat *format, const mrange_t *range, size_t *index) { bool result; /* Bilan à retourner */ const GBinSymbol *prev; /* Symbole précédent */ @@ -1463,7 +1553,7 @@ bool g_binary_format_find_first_symbol_inside(GBinFormat *format, const mrange_t g_rw_lock_reader_lock(&format->syms_lock); - result = __g_binary_format_find_symbol(format, range, (__compar_fn_t)find_symbol, index, NULL); + result = __g_program_format_find_symbol(format, range, (__compar_fn_t)find_symbol, index, NULL); if (result) while (*index > 0) @@ -1502,15 +1592,15 @@ bool g_binary_format_find_first_symbol_inside(GBinFormat *format, const mrange_t * * ******************************************************************************/ -bool g_binary_format_resolve_symbol(GBinFormat *format, const vmpa2t *addr, bool strict, GBinSymbol **symbol, phys_t *diff) +bool g_program_format_resolve_symbol(GProgramFormat *format, const vmpa2t *addr, bool strict, GBinSymbol **symbol, phys_t *diff) { bool result; /* Bilan à retourner */ const mrange_t *range; /* Espace mémoire parcouru */ if (strict) - result = g_binary_format_find_symbol_at(format, addr, symbol); + result = g_program_format_find_symbol_at(format, addr, symbol); else - result = g_binary_format_find_symbol_for(format, addr, symbol); + result = g_program_format_find_symbol_for(format, addr, symbol); if (result) { @@ -1548,7 +1638,7 @@ bool g_binary_format_resolve_symbol(GBinFormat *format, const vmpa2t *addr, bool * * ******************************************************************************/ -void g_binary_format_lock_unlock_errors(GBinFormat *format, bool state) +void g_program_format_lock_unlock_errors(GProgramFormat *format, bool state) { if (state) { @@ -1584,11 +1674,11 @@ void g_binary_format_lock_unlock_errors(GBinFormat *format, bool state) * * ******************************************************************************/ -void g_binary_format_add_error(GBinFormat *format, BinaryFormatError type, const vmpa2t *addr, const char *desc) +void g_program_format_add_error(GProgramFormat *format, BinaryFormatError type, const vmpa2t *addr, const char *desc) { fmt_error *error; /* Raccourci de confort */ - g_binary_format_lock_errors(format); + g_program_format_lock_errors(format); format->errors = realloc(format->errors, ++format->error_count * sizeof(fmt_error)); @@ -1603,7 +1693,7 @@ void g_binary_format_add_error(GBinFormat *format, BinaryFormatError type, const else error->desc = NULL; - g_binary_format_unlock_errors(format); + g_program_format_unlock_errors(format); } @@ -1620,7 +1710,7 @@ void g_binary_format_add_error(GBinFormat *format, BinaryFormatError type, const * * ******************************************************************************/ -size_t g_binary_format_count_errors(GBinFormat *format) +size_t g_program_format_count_errors(GProgramFormat *format) { size_t result; /* Quantité à retourner */ @@ -1649,7 +1739,7 @@ size_t g_binary_format_count_errors(GBinFormat *format) * * ******************************************************************************/ -bool g_binary_format_get_error(GBinFormat *format, size_t index, BinaryFormatError *type, vmpa2t *addr, char **desc) +bool g_program_format_get_error(GProgramFormat *format, size_t index, BinaryFormatError *type, vmpa2t *addr, char **desc) { bool result; /* Bilan à retourner */ fmt_error *error; /* Raccourci de confort */ @@ -1693,7 +1783,7 @@ bool g_binary_format_get_error(GBinFormat *format, size_t index, BinaryFormatErr * * ******************************************************************************/ -static bool g_binary_format_load_errors(GBinFormat *format, packed_buffer_t *pbuf) +static bool g_program_format_load_errors(GProgramFormat *format, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ uleb128_t value; /* Valeur ULEB128 à charger */ @@ -1701,7 +1791,7 @@ static bool g_binary_format_load_errors(GBinFormat *format, packed_buffer_t *pbu fmt_error *error; /* Raccourci de confort */ rle_string str; /* Chaîne à charger */ - g_binary_format_lock_errors(format); + g_program_format_lock_errors(format); result = unpack_uleb128(&value, pbuf); if (!result) goto exit; @@ -1736,7 +1826,7 @@ static bool g_binary_format_load_errors(GBinFormat *format, packed_buffer_t *pbu exit: - g_binary_format_unlock_errors(format); + g_program_format_unlock_errors(format); return result; @@ -1756,14 +1846,14 @@ static bool g_binary_format_load_errors(GBinFormat *format, packed_buffer_t *pbu * * ******************************************************************************/ -static bool g_binary_format_store_errors(GBinFormat *format, packed_buffer_t *pbuf) +static bool g_program_format_store_errors(GProgramFormat *format, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ fmt_error *error; /* Raccourci de confort */ rle_string str; /* Chaîne à conserver */ - g_binary_format_lock_errors(format); + g_program_format_lock_errors(format); result = pack_uleb128((uleb128_t []){ format->error_count }, pbuf); @@ -1785,7 +1875,7 @@ static bool g_binary_format_store_errors(GBinFormat *format, packed_buffer_t *pb } - g_binary_format_unlock_errors(format); + g_program_format_unlock_errors(format); return result; @@ -1812,7 +1902,7 @@ static bool g_binary_format_store_errors(GBinFormat *format, packed_buffer_t *pb * * ******************************************************************************/ -static bool g_binary_format_load(GBinFormat *format, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_program_format_load(GProgramFormat *format, GObjectStorage *storage, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à consulter*/ @@ -1831,7 +1921,7 @@ static bool g_binary_format_load(GBinFormat *format, GObjectStorage *storage, pa UNLOCK_GOBJECT_EXTRA(extra); if (result) - result = g_binary_format_load_start_points(format, pbuf); + result = g_program_format_load_start_points(format, pbuf); if (result) { @@ -1857,7 +1947,7 @@ static bool g_binary_format_load(GBinFormat *format, GObjectStorage *storage, pa if (result) - result = g_binary_format_load_errors(format, pbuf); + result = g_program_format_load_errors(format, pbuf); return result; @@ -1878,7 +1968,7 @@ static bool g_binary_format_load(GBinFormat *format, GObjectStorage *storage, pa * * ******************************************************************************/ -static bool g_binary_format_store(GBinFormat *format, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_program_format_store(GProgramFormat *format, GObjectStorage *storage, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à consulter*/ @@ -1894,7 +1984,7 @@ static bool g_binary_format_store(GBinFormat *format, GObjectStorage *storage, p UNLOCK_GOBJECT_EXTRA(extra); if (result) - result = g_binary_format_store_start_points(format, pbuf); + result = g_program_format_store_start_points(format, pbuf); if (result) { @@ -1913,8 +2003,10 @@ static bool g_binary_format_store(GBinFormat *format, GObjectStorage *storage, p if (result) - result = g_binary_format_store_errors(format, pbuf); + result = g_program_format_store_errors(format, pbuf); return result; } + +#endif diff --git a/src/format/format.h b/src/format/program.h index f9aa430..12b095c 100644 --- a/src/format/format.h +++ b/src/format/program.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * format.h - prototypes pour le support des différents formats binaires + * program.h - prototypes pour le support des différents formats de programmes * - * Copyright (C) 2009-2020 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,12 +21,35 @@ */ -#ifndef _FORMAT_FORMAT_H -#define _FORMAT_FORMAT_H +#ifndef _FORMAT_PROGRAM_H +#define _FORMAT_PROGRAM_H -#include <glib-object.h> #include <stdbool.h> + + +#include "../arch/vmpa.h" +#include "../common/datatypes.h" +#include "../glibext/helpers.h" + + + +#define G_TYPE_PROGRAM_FORMAT (g_program_format_get_type()) + +DECLARE_GTYPE(GProgramFormat, g_program_format, G, PROGRAM_FORMAT); + + +/* Indique le boutisme employé par le format binaire analysé. */ +SourceEndian g_program_format_get_endianness(const GProgramFormat *); + +/* Fournit l'emplacement d'une section donnée. */ +bool g_program_format_find_section_range_by_name(const GProgramFormat *, const char *, mrange_t *); + + + + +#if 0 + #include <sys/types.h> @@ -52,47 +75,31 @@ typedef enum _FormatFlag } FormatFlag; -#define G_TYPE_BIN_FORMAT g_binary_format_get_type() -#define G_BIN_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_BIN_FORMAT, GBinFormat)) -#define G_IS_BIN_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_BIN_FORMAT)) -#define G_BIN_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BIN_FORMAT, GBinFormatClass)) -#define G_IS_BIN_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BIN_FORMAT)) -#define G_BIN_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BIN_FORMAT, GBinFormatClass)) -/* Format binaire générique (instance) */ -typedef struct _GBinFormat GBinFormat; - -/* Format binaire générique (classe) */ -typedef struct _GBinFormatClass GBinFormatClass; - - -/* Indique le type défini pour un format binaire générique. */ -GType g_binary_format_get_type(void); - /* Ajoute une information complémentaire à un format. */ -bool g_binary_format_set_flag(GBinFormat *, FormatFlag); +bool g_program_format_set_flag(GProgramFormat *, FormatFlag); /* Retire une information complémentaire à un format. */ -bool g_binary_format_unset_flag(GBinFormat *, FormatFlag); +bool g_program_format_unset_flag(GProgramFormat *, FormatFlag); /* Détermine si un format possède un fanion particulier. */ -bool g_binary_format_has_flag(const GBinFormat *, FormatFlag); +bool g_program_format_has_flag(const GProgramFormat *, FormatFlag); /* Fournit les particularités du format. */ -FormatFlag g_binary_format_get_flags(const GBinFormat *); +FormatFlag g_program_format_get_flags(const GProgramFormat *); /* Indique le boutisme employé par le format binaire analysé. */ -SourceEndian g_binary_format_get_endianness(const GBinFormat *); +//SourceEndian g_program_format_get_endianness(const GProgramFormat *); /* Enregistre une adresse comme début d'une zone de code. */ -void g_binary_format_register_code_point(GBinFormat *, virt_t, DisassPriorityLevel); +void g_program_format_register_code_point(GProgramFormat *, virt_t, DisassPriorityLevel); /* Intègre dans un contexte les informations tirées d'un format. */ -void g_binary_format_preload_disassembling_context(GBinFormat *, GProcContext *, GtkStatusStack *); +void g_program_format_preload_disassembling_context(GProgramFormat *, GProcContext *, GtkStatusStack *); /* Définit les points de départ d'un contexte de désassemblage. */ -void g_binary_format_activate_disassembling_context(GBinFormat *, GProcContext *, GtkStatusStack *); +void g_program_format_activate_disassembling_context(GProgramFormat *, GProcContext *, GtkStatusStack *); @@ -100,7 +107,7 @@ void g_binary_format_activate_disassembling_context(GBinFormat *, GProcContext * /* Fournit le décodeur de symboles privilégié pour un format. */ -GCompDemangler *g_binary_format_get_demangler(const GBinFormat *); +GCompDemangler *g_program_format_get_demangler(const GProgramFormat *); @@ -108,60 +115,60 @@ GCompDemangler *g_binary_format_get_demangler(const GBinFormat *); /* Protège ou lève la protection de l'accès aux symboles. */ -void g_binary_format_lock_unlock_symbols_rd(GBinFormat *, bool); +void g_program_format_lock_unlock_symbols_rd(GProgramFormat *, bool); -#define g_binary_format_lock_symbols_rd(f) g_binary_format_lock_unlock_symbols_rd(f, true) -#define g_binary_format_unlock_symbols_rd(f) g_binary_format_lock_unlock_symbols_rd(f, false) +#define g_program_format_lock_symbols_rd(f) g_program_format_lock_unlock_symbols_rd(f, true) +#define g_program_format_unlock_symbols_rd(f) g_program_format_lock_unlock_symbols_rd(f, false) /* Protège ou lève la protection de l'accès aux symboles. */ -void g_binary_format_lock_unlock_symbols_wr(GBinFormat *, bool); +void g_program_format_lock_unlock_symbols_wr(GProgramFormat *, bool); -#define g_binary_format_lock_symbols_wr(f) g_binary_format_lock_unlock_symbols_wr(f, true) -#define g_binary_format_unlock_symbols_wr(f) g_binary_format_lock_unlock_symbols_wr(f, false) +#define g_program_format_lock_symbols_wr(f) g_program_format_lock_unlock_symbols_wr(f, true) +#define g_program_format_unlock_symbols_wr(f) g_program_format_lock_unlock_symbols_wr(f, false) /* Assure qu'un verrou est bien posé pour l'accès aux symboles. */ #ifndef NDEBUG -void g_binary_format_check_for_symbols_lock(const GBinFormat *); +void g_program_format_check_for_symbols_lock(const GProgramFormat *); #endif /* Fournit la marque de dernière modification des symboles. */ -unsigned int g_binary_format_get_symbols_stamp(const GBinFormat *); +unsigned int g_program_format_get_symbols_stamp(const GProgramFormat *); /* Compte le nombre de symboles représentés. */ -size_t g_binary_format_count_symbols(const GBinFormat *); +size_t g_program_format_count_symbols(const GProgramFormat *); /* Fournit un symbole lié à un format. */ -GBinSymbol *g_binary_format_get_symbol(const GBinFormat *, size_t); +GBinSymbol *g_program_format_get_symbol(const GProgramFormat *, size_t); /* Ajoute un symbole à la collection du format binaire. */ -bool g_binary_format_add_symbol(GBinFormat *, GBinSymbol *); +bool g_program_format_add_symbol(GProgramFormat *, GBinSymbol *); /* Ajoute plusieurs symboles à la collection du format binaire. */ -bool g_binary_format_add_symbols(GBinFormat *, GBinSymbol **, size_t); +bool g_program_format_add_symbols(GProgramFormat *, GBinSymbol **, size_t); /* Retire un symbole de la collection du format binaire. */ -void g_binary_format_remove_symbol(GBinFormat *, GBinSymbol *); +void g_program_format_remove_symbol(GProgramFormat *, GBinSymbol *); /* Recherche le symbole correspondant à une étiquette. */ -bool g_binary_format_find_symbol_by_label(GBinFormat *, const char *, GBinSymbol **); +bool g_program_format_find_symbol_by_label(GProgramFormat *, const char *, GBinSymbol **); /* Recherche l'indice du symbole correspondant à une adresse. */ -bool g_binary_format_find_symbol_index_at(GBinFormat *, const vmpa2t *, size_t *); +bool g_program_format_find_symbol_index_at(GProgramFormat *, const vmpa2t *, size_t *); /* Recherche le symbole correspondant à une adresse. */ -bool g_binary_format_find_symbol_at(GBinFormat *, const vmpa2t *, GBinSymbol **); +bool g_program_format_find_symbol_at(GProgramFormat *, const vmpa2t *, GBinSymbol **); /* Recherche le symbole contenant une adresse. */ -bool g_binary_format_find_symbol_for(GBinFormat *, const vmpa2t *, GBinSymbol **); +bool g_program_format_find_symbol_for(GProgramFormat *, const vmpa2t *, GBinSymbol **); /* Recherche le symbole suivant celui lié à une adresse. */ -bool g_binary_format_find_next_symbol_at(GBinFormat *, const vmpa2t *, GBinSymbol **); +bool g_program_format_find_next_symbol_at(GProgramFormat *, const vmpa2t *, GBinSymbol **); /* Recherche le premier symbole inclus dans une zone mémoire. */ -bool g_binary_format_find_first_symbol_inside(GBinFormat *, const mrange_t *, size_t *); +bool g_program_format_find_first_symbol_inside(GProgramFormat *, const mrange_t *, size_t *); /* Recherche le symbole correspondant à une adresse. */ -bool g_binary_format_resolve_symbol(GBinFormat *, const vmpa2t *, bool, GBinSymbol **, phys_t *); +bool g_program_format_resolve_symbol(GProgramFormat *, const vmpa2t *, bool, GBinSymbol **, phys_t *); @@ -181,20 +188,21 @@ typedef enum _BinaryFormatError /* Protège ou lève la protection de l'accès aux erreurs. */ -void g_binary_format_lock_unlock_errors(GBinFormat *, bool); +void g_program_format_lock_unlock_errors(GProgramFormat *, bool); -#define g_binary_format_lock_errors(f) g_binary_format_lock_unlock_errors(f, true) -#define g_binary_format_unlock_errors(f) g_binary_format_lock_unlock_errors(f, false) +#define g_program_format_lock_errors(f) g_program_format_lock_unlock_errors(f, true) +#define g_program_format_unlock_errors(f) g_program_format_lock_unlock_errors(f, false) /* Etend la liste des soucis détectés avec de nouvelles infos. */ -void g_binary_format_add_error(GBinFormat *, BinaryFormatError, const vmpa2t *, const char *); +void g_program_format_add_error(GProgramFormat *, BinaryFormatError, const vmpa2t *, const char *); /* Indique le nombre d'erreurs relevées au niveau assembleur. */ -size_t g_binary_format_count_errors(GBinFormat *); +size_t g_program_format_count_errors(GProgramFormat *); /* Fournit les éléments concernant un soucis détecté. */ -bool g_binary_format_get_error(GBinFormat *, size_t, BinaryFormatError *, vmpa2t *, char **); +bool g_program_format_get_error(GProgramFormat *, size_t, BinaryFormatError *, vmpa2t *, char **); +#endif -#endif /* _FORMAT_FORMAT_H */ +#endif /* _FORMAT_PROGRAM_H */ diff --git a/src/framework.h b/src/framework.h deleted file mode 100644 index e8e88fe..0000000 --- a/src/framework.h +++ /dev/null @@ -1,66 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * framework.h - prototypes pour le fichier d'entrée du programme - * - * Copyright (C) 2024 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. - */ - - -#ifndef _FRAMEWORK_H -#define _FRAMEWORK_H - - -#include <glib-object.h> - - - -/* --------------------- DEFINITION D'APPLICATION PERSONNALISEE --------------------- */ - - -#define GTK_TYPE_CHRYSALIDE_FRAMEWORK gtk_chrysalide_framework_get_type() -#define GTK_CHRYSALIDE_FRAMEWORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CHRYSALIDE_FRAMEWORK, GtkChrysalideFramework)) -#define GTK_IS_CHRYSALIDE_FRAMEWORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CHRYSALIDE_FRAMEWORK)) -#define GTK_CHRYSALIDE_FRAMEWORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CHRYSALIDE_FRAMEWORK, GtkChrysalideFrameworkClass)) -#define GTK_IS_CHRYSALIDE_FRAMEWORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CHRYSALIDE_FRAMEWORK)) -#define GTK_CHRYSALIDE_FRAMEWORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CHRYSALIDE_FRAMEWORK, GtkChrysalideFrameworkClass)) - - -/* Définition de l'application principale graphique (instance) */ -typedef struct _GtkChrysalideFramework GtkChrysalideFramework; - -/* Définition de l'application principale graphique (classe) */ -typedef struct _GtkChrysalideFrameworkClass GtkChrysalideFrameworkClass; - - -/* Indique le type défini pour une application principale graphique de Chrysalide. */ -GType gtk_chrysalide_framework_get_type(void); - -/* Crée une nouvelle application principale pour Chrysalide. */ -GtkChrysalideFramework *gtk_chrysalide_framework_new(void); - - - -/* ---------------------- POINT D'ENTREE PRINCIPAL D'EXECUTION ---------------------- */ - - -/* Point d'entrée du programme. */ -int main(int, char **); - - - -#endif /* _FRAMEWORK_H */ diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am index b5ea0f0..b0a7c31 100644 --- a/src/glibext/Makefile.am +++ b/src/glibext/Makefile.am @@ -1,50 +1,74 @@ BUILT_SOURCES = chrysamarshal.h chrysamarshal.c resources.h resources.c -noinst_LTLIBRARIES = libglibext4.la libglibextui.la # libglibext.la - -libglibext_la_SOURCES = \ - comparison-int.h \ - comparison.h comparison.c \ - configuration-int.h \ - configuration.h configuration.c \ - gbinarycursor.h gbinarycursor.c \ - gbinportion-int.h \ - gbinportion.h gbinportion.c \ - glinecursor-int.h \ - glinecursor.h glinecursor.c \ - gnhash.h gnhash.c \ - notifier.h \ - objhole.h \ - proto.h \ - seq.h seq.c \ - _signal.h signal.c \ - singleton.h singleton.c \ - linetoken.h linetoken.c \ - umemslice-int.h \ - umemslice.h umemslice.c - -if BUILD_GTK_SUPPORT - -libglibext_la_SOURCES += \ - gloadedpanel-int.h \ - gloadedpanel.h gloadedpanel.c \ - named-int.h \ - named.h named.c - -endif - -libglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) +noinst_LTLIBRARIES = libglibext.la libglibextui.la + +# libglibext_la_SOURCES = \ +# configuration-int.h \ +# configuration.h configuration.c \ +# gbinarycursor.h gbinarycursor.c \ +# glinecursor-int.h \ +# glinecursor.h glinecursor.c \ +# gnhash.h gnhash.c \ +# notifier.h \ +# \ +# proto.h \ +# seq.h seq.c \ +# singleton.h singleton.c \ +# linetoken.h linetoken.c \ +# umemslice-int.h \ +# umemslice.h umemslice.c + +# if BUILD_GTK_SUPPORT + +# libglibext_la_SOURCES += \ +# gloadedpanel-int.h \ +# gloadedpanel.h gloadedpanel.c \ +# named-int.h \ +# named.h named.c + +# endif + +# libglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) #libglibext_la_LIBADD = \ # generators/libglibextgenerators.la -libglibext4_la_SOURCES = \ +libglibext_la_SOURCES = \ chrysamarshal.h chrysamarshal.c \ - helpers.h - -libglibext4_la_CFLAGS = $(TOOLKIT_CFLAGS) + comparable-int.h \ + comparable.h comparable.c \ + hashable-int.h \ + hashable.h hashable.c \ + helpers.h \ + log-int.h \ + log.h log.c \ + objhole-int.h \ + objhole.h objhole.c \ + portion-int.h \ + portion.h portion.c \ + secstorage-int.h \ + secstorage.h secstorage.c \ + serialize-int.h \ + serialize.h serialize.c \ + sigredir.h sigredir.c \ + singleton-int.h \ + singleton.h singleton.c \ + storage-int.h \ + storage.h storage.c \ + strbuilder-int.h \ + strbuilder.h strbuilder.c \ + tpmem-int.h \ + tpmem.h tpmem.c \ + work-int.h \ + work.h work.c \ + workgroup-int.h \ + workgroup.h workgroup.c \ + workqueue-int.h \ + workqueue.h workqueue.c + +libglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBZIP_CFLAGS) RES_FILES = \ @@ -78,7 +102,7 @@ libglibextui_la_LIBADD = \ devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) -dev_HEADERS = $(libglibext_la_SOURCES:%c=) +dev_HEADERS = $(libglibext_la_SOURCES:%c=) $(libglibextui_la_SOURCES:%c=) SUBDIRS = generators options diff --git a/src/glibext/bufferline.c b/src/glibext/bufferline.c index 4862e9f..e3fb27b 100644 --- a/src/glibext/bufferline.c +++ b/src/glibext/bufferline.c @@ -24,14 +24,16 @@ #include "bufferline.h" +#include <assert.h> +#include <malloc.h> + + #include "bufferline-int.h" #if 0 -#include <assert.h> -#include <malloc.h> #include <string.h> @@ -273,158 +275,12 @@ bool g_buffer_line_create(GBufferLine *line, size_t col_count) } - - - - - - - -/****************************************************************************** -* * -* Paramètres : line = ligne à venir compléter. * -* column = colonne de la ligne visée par l'insertion. * -* tag = type de décorateur à utiliser. * -* text = texte à insérer dans l'existant. * -* length = taille du texte à traiter. * -* style = gestionnaire de paramètres de rendu à consulter. * -* creator = instance GLib quelconque à associer. * -* * -* Description : Ajoute du texte à formater dans une ligne donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_buffer_line_append_text(GBufferLine *line, size_t column, TokenRenderingTag tag, const char *text, size_t length, const GTokenStyle *style, GObject *creator) -{ - size_t index; /* Indice d'insertion */ - //content_origin *origin; /* Définition d'une origine */ - - assert(column < line->col_count); - assert(length > 0); - - index = append_text_to_line_column(&line->columns[column], tag, text, length, style); - - /* - if (creator != NULL) - { - line->origins = realloc(line->origins, ++line->ocount * sizeof(content_origin)); - - origin = &line->origins[line->ocount - 1]; - - origin->coord.column = column; - origin->coord.index = index; - - origin->creator = creator; - g_object_ref(G_OBJECT(creator)); - - } - */ - -} - - - - - - - - - - - -/****************************************************************************** -* * -* Paramètres : line = ligne de texte à manipuler. * -* cr = contexte graphique dédié à la procédure. * -* column = (première) colonne à traiter. * -* y = ordonnée du point d'impression. * -* style = style de rendu pour les bribes de texte. * -* * -* Description : Imprime la ligne de texte représentée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_buffer_line_draw(const GBufferLine *line, cairo_t *cr, size_t column, int y, const GTokenStyle *style) -{ -#if 0 - GBufferLineClass *class; /* Stockage de briques de base */ - bool has_src_surface; /* Note une présence définie */ -#endif - size_t max_column; /* Borne de fin des colonnes */ - int x; /* Point de départ d'impression*/ - size_t i; /* Boucle de parcours */ - - /* - if (line->flags != BLF_NONE) - { - class = G_BUFFER_LINE_GET_CLASS(line); - - if (line->flags & BLF_ENTRYPOINT) - { - cairo_set_source_surface(cairo, class->entrypoint_img, 5, y); - has_src_surface = true; - } - else if (line->flags & BLF_BOOKMARK) - { - cairo_set_source_surface(cairo, class->bookmark_img, 5, y); - has_src_surface = true; - } - else - has_src_surface = false; - - if (has_src_surface) - cairo_paint(cairo); - - } - */ - - - /* Détermination de l'éventail des colonnes à traiter */ - - if (column == line->merge_start) - max_column = line->col_count; - - else if (column > line->merge_start) - max_column = 0; - - else - max_column = column + 1; - - /* Dessin du contenu de ces colonnes */ - - x = 0; - - for (i = column; i < max_column; i++) - draw_line_column(&line->columns[i], cr, &x, y, style); - -} - - - - - - - - - - -#if 0 - - /****************************************************************************** * * -* Paramètres : line = ligne à venir compléter. * -* col = indice de la colonne à constituer. * -* size = taille souhaitée de l'impression des positions. * -* addr = localisation physique à venir représenter. * +* Paramètres : line = ligne à venir compléter. * +* column = indice de la colonne visée par l'insertion. * +* size = taille souhaitée de l'impression des positions. * +* addr = localisation physique à venir représenter. * * * * Description : Construit le tronc commun d'une ligne autour de sa position. * * * @@ -434,7 +290,7 @@ void g_buffer_line_draw(const GBufferLine *line, cairo_t *cr, size_t column, int * * ******************************************************************************/ -void g_buffer_line_fill_phys(GBufferLine *line, size_t col, MemoryDataSize size, const vmpa2t *addr) +void g_buffer_line_fill_physical(GBufferLine *line, size_t column, MemoryDataSize size, const vmpa2t *addr) { VMPA_BUFFER(position); /* Emplacement au format texte */ size_t len; /* Taille de l'élément inséré */ @@ -448,20 +304,22 @@ void g_buffer_line_fill_phys(GBufferLine *line, size_t col, MemoryDataSize size, if (i == len) i = len - 1; + assert(column < line->col_count); + if (i > 0) - g_buffer_line_append_text(line, col, position, i, RTT_PHYS_ADDR_PAD, NULL); + g_buffer_line_append_text(line, column, TRT_PHYS_ADDR_PAD, position, i, NULL, NULL); - g_buffer_line_append_text(line, col, &position[i], len - i, RTT_PHYS_ADDR, NULL); + g_buffer_line_append_text(line, column, TRT_PHYS_ADDR, &position[i], len - i, NULL, NULL); } /****************************************************************************** * * -* Paramètres : line = ligne à venir compléter. * -* col = indice de la colonne à constituer. * -* size = taille souhaitée de l'impression des positions. * -* addr = localisation virtuelle à venir représenter. * +* Paramètres : line = ligne à venir compléter. * +* column = indice de la colonne visée par l'insertion. * +* size = taille souhaitée de l'impression des positions. * +* addr = localisation virtuelle à venir représenter. * * * * Description : Construit le tronc commun d'une ligne autour de sa position. * * * @@ -471,7 +329,7 @@ void g_buffer_line_fill_phys(GBufferLine *line, size_t col, MemoryDataSize size, * * ******************************************************************************/ -void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, const vmpa2t *addr) +void g_buffer_line_fill_virtual(GBufferLine *line, size_t column, MemoryDataSize size, const vmpa2t *addr) { VMPA_BUFFER(position); /* Emplacement au format texte */ size_t len; /* Taille de l'élément inséré */ @@ -479,6 +337,8 @@ void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, vmpa2_virt_to_string(addr, size, position, &len); + assert(column < line->col_count); + if (has_virt_addr(addr)) { for (i = 2; i < len; i++) @@ -488,14 +348,14 @@ void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, i = len - 1; if (i > 0) - g_buffer_line_append_text(line, col, position, i, RTT_VIRT_ADDR_PAD, NULL); + g_buffer_line_append_text(line, column, TRT_VIRT_ADDR_PAD, position, i, NULL, NULL); - g_buffer_line_append_text(line, col, &position[i], len - i, RTT_VIRT_ADDR, NULL); + g_buffer_line_append_text(line, column, TRT_VIRT_ADDR, &position[i], len - i, NULL, NULL); } else - g_buffer_line_append_text(line, col, position, len, RTT_VIRT_ADDR_PAD, NULL); + g_buffer_line_append_text(line, column, TRT_VIRT_ADDR_PAD, position, len, NULL, NULL); } @@ -503,7 +363,7 @@ void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, /****************************************************************************** * * * Paramètres : line = ligne à venir compléter. * -* col = indice de la colonne à constituer. * +* column = indice de la colonne visée par l'insertion. * * content = contenu binaire global à venir lire. * * range = localisation des données à venir lire et présenter.* * max = taille maximale de la portion binaire en octets. * @@ -516,7 +376,7 @@ void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, * * ******************************************************************************/ -void g_buffer_line_fill_content(GBufferLine *line, size_t col, const GBinContent *content, const mrange_t *range, phys_t max) +void g_buffer_line_fill_content(GBufferLine *line, size_t column, const GBinContent *content, const mrange_t *range, phys_t max) { phys_t length; /* Taille de la couverture */ bool truncated; /* Indique si le code est coupé*/ @@ -548,7 +408,7 @@ void g_buffer_line_fill_content(GBufferLine *line, size_t col, const GBinContent if (required <= sizeof(static_buffer)) bin_code = static_buffer; else - bin_code = (char *)calloc(required, sizeof(char)); + bin_code = calloc(required, sizeof(char)); /* Code brut */ @@ -589,7 +449,7 @@ void g_buffer_line_fill_content(GBufferLine *line, size_t col, const GBinContent /* Conclusion */ - g_buffer_line_append_text(line, col, bin_code, iter - bin_code, RTT_RAW_CODE, NULL); + g_buffer_line_append_text(line, column, TRT_RAW_CODE, bin_code, iter - bin_code, NULL, NULL); if (bin_code != static_buffer) free(bin_code); @@ -599,6 +459,146 @@ void g_buffer_line_fill_content(GBufferLine *line, size_t col, const GBinContent /****************************************************************************** * * +* Paramètres : line = ligne à venir compléter. * +* column = colonne de la ligne visée par l'insertion. * +* tag = type de décorateur à utiliser. * +* text = texte à insérer dans l'existant. * +* length = taille du texte à traiter. * +* style = gestionnaire de paramètres de rendu à consulter. * +* creator = instance GLib quelconque à associer. * +* * +* Description : Ajoute du texte à formater dans une ligne donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_append_text(GBufferLine *line, size_t column, TokenRenderingTag tag, const char *text, size_t length, const GTokenStyle *style, GObject *creator) +{ + size_t index; /* Indice d'insertion */ + //content_origin *origin; /* Définition d'une origine */ + + assert(column < line->col_count); + assert(length > 0); + + index = append_text_to_line_column(&line->columns[column], tag, text, length, style); + + /* + if (creator != NULL) + { + line->origins = realloc(line->origins, ++line->ocount * sizeof(content_origin)); + + origin = &line->origins[line->ocount - 1]; + + origin->coord.column = column; + origin->coord.index = index; + + origin->creator = creator; + g_object_ref(G_OBJECT(creator)); + + } + */ + +} + + + + + + + + + + + +/****************************************************************************** +* * +* Paramètres : line = ligne de texte à manipuler. * +* cr = contexte graphique dédié à la procédure. * +* column = (première) colonne à traiter. * +* y = ordonnée du point d'impression. * +* style = style de rendu pour les bribes de texte. * +* * +* Description : Imprime la ligne de texte représentée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_draw(const GBufferLine *line, cairo_t *cr, size_t column, int y, const GTokenStyle *style) +{ +#if 0 + GBufferLineClass *class; /* Stockage de briques de base */ + bool has_src_surface; /* Note une présence définie */ +#endif + size_t max_column; /* Borne de fin des colonnes */ + int x; /* Point de départ d'impression*/ + size_t i; /* Boucle de parcours */ + + /* + if (line->flags != BLF_NONE) + { + class = G_BUFFER_LINE_GET_CLASS(line); + + if (line->flags & BLF_ENTRYPOINT) + { + cairo_set_source_surface(cairo, class->entrypoint_img, 5, y); + has_src_surface = true; + } + else if (line->flags & BLF_BOOKMARK) + { + cairo_set_source_surface(cairo, class->bookmark_img, 5, y); + has_src_surface = true; + } + else + has_src_surface = false; + + if (has_src_surface) + cairo_paint(cairo); + + } + */ + + + /* Détermination de l'éventail des colonnes à traiter */ + + if (column == line->merge_start) + max_column = line->col_count; + + else if (column > line->merge_start) + max_column = 0; + + else + max_column = column + 1; + + /* Dessin du contenu de ces colonnes */ + + x = 0; + + for (i = column; i < max_column; i++) + draw_line_column(&line->columns[i], cr, &x, y, style); + +} + + + + + + + + + + +#if 0 + + + +/****************************************************************************** +* * * Paramètres : line = ligne à venir consulter. * * column = indice de la colonne visée par les recherches. * * * diff --git a/src/glibext/bufferline.h b/src/glibext/bufferline.h index d6a2e2d..e95ee2b 100644 --- a/src/glibext/bufferline.h +++ b/src/glibext/bufferline.h @@ -30,6 +30,8 @@ #include "helpers.h" #include "tokenstyle.h" +#include "../arch/vmpa.h" +#include "../analysis/content.h" @@ -45,8 +47,6 @@ #ifdef INCLUDE_GTK_SUPPORT # include "widthtracker.h" #endif -#include "../analysis/content.h" -#include "../arch/vmpa.h" @@ -75,6 +75,9 @@ typedef struct _GBufferLineClass GBufferLineClass; + + + #define G_TYPE_BUFFER_LINE (g_buffer_line_get_type()) DECLARE_GTYPE(GBufferLine, g_buffer_line, G, BUFFER_LINE); @@ -98,7 +101,14 @@ typedef enum _BufferLineFlags /* Crée une nouvelle représentation de fragments de texte. */ GBufferLine *g_buffer_line_new(size_t); +/* Construit le tronc commun d'une ligne autour de sa position. */ +void g_buffer_line_fill_physical(GBufferLine *, size_t, MemoryDataSize, const vmpa2t *); + +/* Construit le tronc commun d'une ligne autour de sa position. */ +void g_buffer_line_fill_virtual(GBufferLine *, size_t, MemoryDataSize, const vmpa2t *); +/* Construit le tronc commun d'une ligne autour de son contenu. */ +void g_buffer_line_fill_content(GBufferLine *, size_t, const GBinContent *, const mrange_t *, phys_t); /* Ajoute du texte à formater dans une ligne donnée. */ void g_buffer_line_append_text(GBufferLine *, size_t, TokenRenderingTag, const char *, size_t, const GTokenStyle *, GObject *); diff --git a/src/glibext/comparable-int.h b/src/glibext/comparable-int.h new file mode 100644 index 0000000..18972c2 --- /dev/null +++ b/src/glibext/comparable-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable-int.h - définitions internes propres aux opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_COMPARABLE_INT_H +#define _GLIBEXT_COMPARABLE_INT_H + + +#include "comparable.h" + + + +/* Réalise une comparaison étendue entre objets. */ +typedef int (* compare_object_fc) (const GComparableObject *, const GComparableObject *); + + +/* Instance d'objet comparable (interface) */ +struct _GComparableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + compare_object_fc compare; /* Comparaison étendue */ + +}; + + + +#endif /* _GLIBEXT_COMPARABLE_INT_H */ diff --git a/src/glibext/comparable.c b/src/glibext/comparable.c new file mode 100644 index 0000000..40fd110 --- /dev/null +++ b/src/glibext/comparable.c @@ -0,0 +1,124 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "comparable.h" + + +#include "comparable-int.h" +#include "../common/sort.h" + + + +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_comparable_object_default_init(GComparableObjectInterface *); + + + +/* Détermine le type d'une interface pour un objet comparable. */ +G_DEFINE_INTERFACE(GComparableObject, g_comparable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de comparaison. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_comparable_object_default_init(GComparableObjectInterface *iface) +{ + iface->compare = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int g_comparable_object_compare(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + GType type_a; /* Type de l'object A */ + GType type_b; /* Type de l'object B */ + GComparableObjectInterface *iface; /* Interface utilisée */ + + type_a = G_OBJECT_TYPE(G_OBJECT(object)); + type_b = G_OBJECT_TYPE(G_OBJECT(other)); + + assert(sizeof(GType) <= sizeof(unsigned long)); + + result = sort_unsigned_long(type_a, type_b); + + if (result == 0) + { + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + result = iface->compare(object, other); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Détermine si deux objets sont fonctionnellement identiques. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +gboolean g_comparable_object_is_equal(const GComparableObject *object, const GComparableObject *other) +{ + gboolean result; /* Bilan à renvoyer */ + int ret; /* Bilan d'une comparaison */ + + ret = g_comparable_object_compare(object, other); + + result = (ret == 0); + + return result; + +} diff --git a/src/glibext/comparable.h b/src/glibext/comparable.h new file mode 100644 index 0000000..7a652ff --- /dev/null +++ b/src/glibext/comparable.h @@ -0,0 +1,46 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour les opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_COMPARABLE_H +#define _GLIBEXT_COMPARABLE_H + + +#include "helpers.h" + + + +#define G_TYPE_COMPARABLE_OBJECT (g_comparable_object_get_type()) + +DECLARE_INTERFACE(GComparableObject, g_comparable_object, G, COMPARABLE_OBJECT); + + + +/* Réalise une comparaison étendue entre objets. */ +int g_comparable_object_compare(const GComparableObject *, const GComparableObject *); + +/* Détermine si deux objets sont fonctionnellement identiques. */ +gboolean g_comparable_object_is_equal(const GComparableObject *, const GComparableObject *); + + + +#endif /* _GLIBEXT_COMPARABLE_H */ diff --git a/src/glibext/comparison-int.h b/src/glibext/comparison-int.h deleted file mode 100644 index 446f25d..0000000 --- a/src/glibext/comparison-int.h +++ /dev/null @@ -1,58 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison-int.h - définitions internes propres aux opérations de comparaison d'objets - * - * Copyright (C) 2022 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. - */ - - -#ifndef _GLIBEXT_COMPARISON_INT_H -#define _GLIBEXT_COMPARISON_INT_H - - -#include "comparison.h" - - - -/* Réalise une comparaison entre objets selon un critère précis. */ -typedef bool (* compare_rich_fc) (const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); - - -/* Instance d'élément comparable (interface) */ -struct _GComparableItemIface -{ - GTypeInterface base_iface; /* A laisser en premier */ - - compare_rich_fc cmp_rich; /* Comparaison de façon précise*/ - -}; - - -/* Redéfinition */ -typedef GComparableItemIface GComparableItemInterface; - - -/* Réalise une comparaison riche entre valeurs entière. */ -bool compare_rich_integer_values_signed(long long, long long, RichCmpOperation); - -/* Réalise une comparaison riche entre valeurs entière. */ -bool compare_rich_integer_values_unsigned(unsigned long long, unsigned long long, RichCmpOperation); - - - -#endif /* _GLIBEXT_COMPARISON_INT_H */ diff --git a/src/glibext/comparison.c b/src/glibext/comparison.c deleted file mode 100644 index 8ce6941..0000000 --- a/src/glibext/comparison.c +++ /dev/null @@ -1,199 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - opérations de comparaison d'objets - * - * Copyright (C) 2022 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Foobar. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "comparison.h" - - -#include <assert.h> - - -#include "comparison-int.h" - - - -/* Procède à l'initialisation de l'interface de comparaison. */ -static void g_comparable_item_default_init(GComparableItemInterface *); - - - -/* Détermine le type d'une interface pour un objet comparable. */ -G_DEFINE_INTERFACE(GComparableItem, g_comparable_item, G_TYPE_OBJECT) - - -/****************************************************************************** -* * -* Paramètres : iface = interface GLib à initialiser. * -* * -* Description : Procède à l'initialisation de l'interface de comparaison. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_comparable_item_default_init(GComparableItemInterface *iface) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : item = premier objet à consulter pour une comparaison. * -* other = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * -* * -* Description : Réalise une comparaison entre objets selon un critère précis.* -* * -* Retour : true si la comparaison a pu être effectuée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) -{ - bool result; /* Etat à retourner */ - GComparableItemIface *iface; /* Interface utilisée */ - - iface = G_COMPARABLE_ITEM_GET_IFACE(item); - - result = iface->cmp_rich(item, other, op, status); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier élément à consulter pour une comparaison. * -* b = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* * -* Description : Réalise une comparaison riche entre valeurs entière. * -* * -* Retour : Bilan des opérations de comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool compare_rich_integer_values_signed(long long a, long long b, RichCmpOperation op) -{ - bool result; /* Bilan à retourner */ - - switch (op) - { - case RCO_LT: - result = (a < b); - break; - - case RCO_LE: - result = (a <= b); - break; - - case RCO_EQ: - result = (a == b); - break; - - case RCO_NE: - result = (a != b); - break; - - case RCO_GT: - result = (a > b); - break; - - case RCO_GE: - result = (a >= b); - break; - - default: - assert(false); - result = false; - break; - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier élément à consulter pour une comparaison. * -* b = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* * -* Description : Réalise une comparaison riche entre valeurs entière. * -* * -* Retour : Bilan des opérations de comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool compare_rich_integer_values_unsigned(unsigned long long a, unsigned long long b, RichCmpOperation op) -{ - bool result; /* Bilan à retourner */ - - switch (op) - { - case RCO_LT: - result = (a < b); - break; - - case RCO_LE: - result = (a <= b); - break; - - case RCO_EQ: - result = (a == b); - break; - - case RCO_NE: - result = (a != b); - break; - - case RCO_GT: - result = (a > b); - break; - - case RCO_GE: - result = (a >= b); - break; - - default: - assert(false); - result = false; - break; - - } - - return result; - -} diff --git a/src/glibext/comparison.h b/src/glibext/comparison.h deleted file mode 100644 index 8d43210..0000000 --- a/src/glibext/comparison.h +++ /dev/null @@ -1,80 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour les opérations de comparaison d'objets - * - * Copyright (C) 2022 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Foobar. If not, see <http://www.gnu.org/licenses/>. - */ - - -#ifndef _GLIBEXT_COMPARISON_H -#define _GLIBEXT_COMPARISON_H - - -#include <glib-object.h> -#include <stdbool.h> - - - -#define G_TYPE_COMPARABLE_ITEM (g_comparable_item_get_type()) -#define G_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_COMPARABLE_ITEM, GComparableItem)) -#define G_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_COMPARABLE_ITEM, GComparableItemIface)) -#define G_IS_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_COMPARABLE_ITEM)) -#define G_IS_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_COMPARABLE_ITEM)) -#define G_COMPARABLE_ITEM_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_COMPARABLE_ITEM, GComparableItemIface)) - - -/* Instance d'élément comparable (coquille vide) */ -typedef struct _GComparableItem GComparableItem; - -/* Instance d'élément comparable (interface) */ -typedef struct _GComparableItemIface GComparableItemIface; - - -/* Modes de comparaison */ -typedef enum _RichCmpOperation -{ - RCO_LT, /* Equivalent de '<' */ - RCO_LE, /* Equivalent de '<=' */ - RCO_EQ, /* Equivalent de '==' */ - RCO_NE, /* Equivalent de '!=' */ - RCO_GT, /* Equivalent de '>' */ - RCO_GE, /* Equivalent de '>°' */ - -} RichCmpOperation; - -/* Détermination d'un besoin de comparaison supplémentaire */ -#define STATUS_NOT_EQUAL(_s, _o) \ - ({ \ - bool __result; \ - if (_o == RCO_LE || _o == RCO_EQ || _o == RCO_GE) \ - __result = !_s; \ - else \ - __result = _s; \ - __result; \ - }) - - -/* Détermine le type d'une interface pour un objet comparable. */ -GType g_comparable_item_get_type(void) G_GNUC_CONST; - -/* Réalise une comparaison entre objets selon un critère précis. */ -bool g_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); - - - -#endif /* _GLIBEXT_COMPARISON_H */ diff --git a/src/glibext/hashable-int.h b/src/glibext/hashable-int.h new file mode 100644 index 0000000..f8a85e1 --- /dev/null +++ b/src/glibext/hashable-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable-int.h - définitions internes propres aux calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_HASHABLE_INT_H +#define _GLIBEXT_HASHABLE_INT_H + + +#include "hashable.h" + + + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +typedef guint (* hash_object_fc) (const GHashableObject *); + + +/* Instance d'objet visant à être unique (interface) */ +struct _GHashableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + hash_object_fc hash; /* Réduction en valeur */ + +}; + + + +#endif /* _GLIBEXT_HASHABLE_INT_H */ diff --git a/src/glibext/hashable.c b/src/glibext/hashable.c new file mode 100644 index 0000000..f988a90 --- /dev/null +++ b/src/glibext/hashable.c @@ -0,0 +1,82 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.c - calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "hashable.h" + + +#include "hashable-int.h" + + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_hashable_object_default_init(GHashableObjectInterface *); + + + +/* Détermine le type d'une interface pour la réduction d'un objet à une valeur. */ +G_DEFINE_INTERFACE(GHashableObject, g_hashable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_hashable_object_default_init(GHashableObjectInterface *iface) +{ + iface->hash = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = objet dont l'instance est à consulter. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +guint g_hashable_object_hash(const GHashableObject *object) +{ + guint result; /* Valeur à retourner */ + GHashableObjectInterface *iface; /* Interface utilisée */ + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + result = iface->hash(object); + + return result; + +} diff --git a/src/glibext/hashable.h b/src/glibext/hashable.h new file mode 100644 index 0000000..165c744 --- /dev/null +++ b/src/glibext/hashable.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.h - prototypes pour les calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_HASHABLE_H +#define _GLIBEXT_HASHABLE_H + + +#include "helpers.h" + + + +#define G_TYPE_HASHABLE_OBJECT (g_hashable_object_get_type()) + +DECLARE_INTERFACE(GHashableObject, g_hashable_object, G, HASHABLE_OBJECT); + + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +guint g_hashable_object_hash(const GHashableObject *); + + + +#endif /* _GLIBEXT_HASHABLE_H */ diff --git a/src/glibext/helpers.h b/src/glibext/helpers.h index 04e51ad..6176245 100644 --- a/src/glibext/helpers.h +++ b/src/glibext/helpers.h @@ -29,6 +29,9 @@ #include <glib-object.h> +#include "../common/compiler.h" + + /** * Les définitions issues de <glib-2.80>/gobject/gtype.h fournissent des macros @@ -118,6 +121,31 @@ } +/** + * Définition sous condition d'une inclusion d'interface. Cette inclusion se réalise + * lorsque la fonction d'initialisation renseignée est définie. + * + * Cette version étendue de la macro G_IMPLEMENT_INTERFACE d'origine est principalement + * pour les raffinements d'objets en forme graphique. + */ + +#define G_IMPLEMENT_INTERFACE_IF_SYM(iface_tp_getter, iface_init) \ + do \ + { \ + extern GType iface_tp_getter(void) __weak; \ + extern void iface_init(GTypeInterface *, gpointer) __weak; \ + if (&iface_tp_getter != NULL && &iface_init != NULL) \ + { \ + GType iface_type = iface_tp_getter(); \ + const GInterfaceInfo implementation_info = { \ + (GInterfaceInitFunc)(void (*)(void))iface_init, NULL, NULL \ + }; \ + g_type_add_interface_static(g_define_type_id, iface_type, &implementation_info);\ + } \ + } \ + while (0); + + /** * Les principales fonctions incrémentant ou réduisant le nombre de références @@ -193,4 +221,5 @@ #endif + #endif /* _GLIBEXT_HELPERS_H */ diff --git a/src/glibext/log-int.h b/src/glibext/log-int.h new file mode 100644 index 0000000..3b3208a --- /dev/null +++ b/src/glibext/log-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * log-int.h - prototypes internes pour la conservation à destination graphique des éléments de journalisation + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_LOG_INT_H +#define _GLIBEXT_LOG_INT_H + + +#include <stdbool.h> + + +#include "log.h" + + + +/* Définition d'une conservation d'objets construits (instance) */ +struct _GLogEntry +{ + GObject parent; /* A laisser en premier */ + + LogMessageType type; /* Type de message porté */ + char *msg; /* Contenu du message diffusé */ + +}; + +/* Définition d'une conservation d'objets construits (classe) */ +struct _GLogEntryClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une conservation pour élément de journalisation. */ +bool g_log_entry_create(GLogEntry *, LogMessageType, const char *); + + + +#endif /* _GLIBEXT_LOG_INT_H */ diff --git a/src/glibext/log.c b/src/glibext/log.c new file mode 100644 index 0000000..039172c --- /dev/null +++ b/src/glibext/log.c @@ -0,0 +1,306 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * log.c - conservation hors mémoire d'objets choisis + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "log.h" + + +#include <malloc.h> +#include <string.h> + + +#include "log-int.h" + + + +/* ---------------------------- ENTREE DE JOURNALISATION ---------------------------- */ + + +/* Liste des propriétés */ + +typedef enum _LogEntryProperty { + + PROP_0, /* Réservé */ + + PROP_ICON_NAME, /* Nom d'image de représentat° */ + PROP_MESSAGE, /* Contenu du message diffusé */ + + N_PROPERTIES + +} LogEntryProperty; + +static GParamSpec *_log_entry_properties[N_PROPERTIES] = { NULL, }; + + +/* Initialise la classe des conservations d'objets en place. */ +static void g_log_entry_class_init(GLogEntryClass *); + +/* Initialise une instance de conservation d'objets en place. */ +static void g_log_entry_init(GLogEntry *); + +/* Supprime toutes les références externes. */ +static void g_log_entry_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_log_entry_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Fournit la valeur d'une propriété d'instance GObject. */ +static void gtk_log_entry_get_property(GObject *, guint, GValue *, GParamSpec *); + + + +/* ---------------------------------------------------------------------------------- */ +/* ENTREE DE JOURNALISATION */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une conservation d'objets construits. */ +G_DEFINE_TYPE(GLogEntry, g_log_entry, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des conservations d'objets en place. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_log_entry_class_init(GLogEntryClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_log_entry_dispose; + object->finalize = g_log_entry_finalize; + object->get_property = gtk_log_entry_get_property; + + _log_entry_properties[PROP_ICON_NAME] = + g_param_spec_string("icon-name", NULL, NULL, + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _log_entry_properties[PROP_MESSAGE] = + g_param_spec_string("message", NULL, NULL, + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object, N_PROPERTIES, _log_entry_properties); + +} + + +/****************************************************************************** +* * +* Paramètres : entry = instance à initialiser. * +* * +* Description : Initialise une instance de conservation d'objets en place. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_log_entry_init(GLogEntry *entry) +{ + entry->type = LMT_COUNT; + entry->msg = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_log_entry_dispose(GObject *object) +{ + G_OBJECT_CLASS(g_log_entry_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_log_entry_finalize(GObject *object) +{ + GLogEntry *entry; /* Version spécialisée */ + + entry = G_LOG_ENTRY(object); + + if (entry->msg != NULL) + free(entry->msg); + + G_OBJECT_CLASS(g_log_entry_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : type = espèce du message à représenter. * +* msg = message à faire apparaître à l'écran. * +* * +* Description : Crée une conservation pour un élément de journalisation. * +* * +* Retour : Conservation mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GLogEntry *g_log_entry_new(LogMessageType type, const char *msg) +{ + GLogEntry *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_LOG_ENTRY, NULL); + + if (!g_log_entry_create(result, type, msg)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : entry = instance de conservation à initialiser pleinement. * +* type = espèce du message à représenter. * +* msg = message à faire apparaître à l'écran. * +* * +* Description : Met en place une conservation pour élément de journalisation.* +* * +* Retour : Conservation mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_log_entry_create(GLogEntry *entry, LogMessageType type, const char *msg) +{ + bool result; /* Bilan à retourner */ + + result = true; + + entry->type = type; + entry->msg = strdup(msg); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à transmettre. [OUT] * +* pspec = définition de la propriété. * +* * +* Description : Fournit la valeur d'une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_log_entry_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GLogEntry *entry; /* Version spécialisée */ + + entry = G_LOG_ENTRY(object); + + switch (prop_id) + { + case PROP_ICON_NAME: + switch (entry->type) + { + case LMT_INFO: + g_value_set_string(value, "dialog-information-symbolic"); + break; + + case LMT_PROCESS: + g_value_set_string(value, "system-run-symbolic"); + break; + + case LMT_WARNING: + case LMT_BAD_BINARY: + g_value_set_string(value, "dialog-warning-symbolic"); + break; + + case LMT_ERROR: + case LMT_EXT_ERROR: + g_value_set_string(value, "computer-fail-symbolic"); + break; + + + case LMT_COUNT: + g_value_set_string(value, "dialog-question-symbolic"); + break; + + } + break; + + case PROP_MESSAGE: + g_value_set_string(value, entry->msg); + break; + + } + +} diff --git a/src/glibext/log.h b/src/glibext/log.h new file mode 100644 index 0000000..472773c --- /dev/null +++ b/src/glibext/log.h @@ -0,0 +1,43 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * log.h - prototypes pour la conservation à destination graphique des éléments de journalisation + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_LOG_H +#define _GLIBEXT_LOG_H + + +#include "helpers.h" +#include "../core/logs.h" + + + +#define G_TYPE_LOG_ENTRY (g_log_entry_get_type()) + +DECLARE_GTYPE(GLogEntry, g_log_entry, G, LOG_ENTRY); + + +/* Crée une conservation pour un élément de journalisation. */ +GLogEntry *g_log_entry_new(LogMessageType, const char *); + + + +#endif /* _GLIBEXT_LOG_H */ diff --git a/src/glibext/objhole-int.h b/src/glibext/objhole-int.h new file mode 100644 index 0000000..dbfb412 --- /dev/null +++ b/src/glibext/objhole-int.h @@ -0,0 +1,169 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.h - définitions internes pour ll'utilisation d'un espace inutilisé dans la structure GObject + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_OBJHOLE_INT_H +#define _GLIBEXT_OBJHOLE_INT_H + + +#include "objhole.h" + + +#include "../common/cpp.h" + + + +/** + * Une structure GObject a la définition suivante : + * + * struct _GObject + * { + * GTypeInstance g_type_instance; + * volatile guint ref_count; + * GData *qdata; + * }; + * + * Chaque objet GLib alloué comporte ainsi 4 octets inutilisés : + * + * (gdb) pt /o GObject + * type = struct _GObject { + * / 0 | 8 / GTypeInstance g_type_instance; + * / 8 | 4 / guint ref_count; + * / XXX 4-byte hole / + * / 16 | 8 / GData *qdata; + * + * / total size (bytes): 24 / + * } + * + * La situation n'a pas échappé aux développeurs GLib, avec la définition réelle + * de la structure (cf. https://github.com/GNOME/glib/blob/main/gobject/gobject.c) : + * + * #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8 + * #define HAVE_OPTIONAL_FLAGS + * #endif + * + * typedef struct + * { + * GTypeInstance g_type_instance; + * guint ref_count; + * #ifdef HAVE_OPTIONAL_FLAGS + * guint optional_flags; + * #endif + * GData *qdata; + * } GObjectReal; + * + * G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal)); + * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count)); + * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata)); + * + * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois. + */ + +/** + * Les bits effectivements utilisés par GLib s'identifie ainsi (02/12/24) : + * + * $ wget -qO /dev/stdout https://raw.githubusercontent.com/GNOME/glib/refs/heads/main/gobject/gobject.c \ + * | grep ' * #define OPTIONAL_FLAG_' + * #define OPTIONAL_FLAG_IN_CONSTRUCTION (1 << 0) + * #define OPTIONAL_FLAG_HAS_SIGNAL_HANDLER (1 << 1) + * #define OPTIONAL_FLAG_HAS_NOTIFY_HANDLER (1 << 2) + * #define OPTIONAL_FLAG_LOCK (1 << 3) + * #define OPTIONAL_FLAG_EVER_HAD_WEAK_REF (1 << 4) + * + * Les n premiers bits doivent ainsi être préservés, même s'il est possible de + * partager le bit de verrouilage OPTIONAL_FLAG_LOCK. + */ + + +#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + +# if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P >= 8 +# define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 1 +# else +# define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 0 +# endif + +#endif + + +#define GOBJECT_LOCK_BIT 3 + + +/* Nouvelle version dense des objets (instance) */ +typedef struct _GThickObject +{ + /** + * (cf. structure GObjectReal officielle). + */ + + GTypeInstance g_type_instance; /* Type d'objet */ + guint ref_count; /* Décompte des références */ + +#ifdef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + guint extra; /* Zone partagée avec GLib */ +#endif + + GData *qdata; /* Données complémentaires ? */ + +#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + guint extra; /* Zone supplémentaire propre */ +#endif + +} GThickObject; + +/* Nouvelle version dense des objets (classe) */ +struct _GThickObjectClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/** + * Définition du périmètre et des moyens d'accès. + */ + +/* GLib 2.83.0 - cfa36f5e9 */ +#define GOBJECT_RESERVED_EXTRA_BITS 5 + +#define GET_GOBJECT_EXTRA(obj, tp) \ + ({ \ + BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ + tp ___result; \ + guint __val; \ + __val = g_thick_object_get_extra(G_THICK_OBJECT(obj)); \ + ___result = *(tp *)(guint []){ __val }; \ + ___result; \ + }) + +#define SET_GOBJECT_EXTRA(obj, tp, data) \ + ({ \ + BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ + BUILD_BUG_ON(sizeof(data) > sizeof(guint *)); \ + guint __val; \ + __val = *(guint *)data; \ + g_thick_object_set_extra(G_THICK_OBJECT(obj), __val); \ + }) + + + +#endif /* _GLIBEXT_OBJHOLE_INT_H */ diff --git a/src/glibext/objhole.c b/src/glibext/objhole.c new file mode 100644 index 0000000..fd6fbc9 --- /dev/null +++ b/src/glibext/objhole.c @@ -0,0 +1,256 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.c - utilisation d'un espace inutilisé dans la structure GObject + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "objhole.h" + + +#include <assert.h> + + +#include "objhole-int.h" + + + +/* Initialise la classe des objets à la structure dense. */ +static void g_thick_object_class_init(GThickObjectClass *); + +/* Initialise une instance d'objet à la structure dense. */ +static void g_thick_object_init(GThickObject *); + +/* Supprime toutes les références externes. */ +static void g_thick_object_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_thick_object_finalize(GObject *); + + + +G_DEFINE_TYPE(GThickObject, g_thick_object, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des objets à la structure dense. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_class_init(GThickObjectClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_thick_object_dispose; + object->finalize = g_thick_object_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance à initialiser. * +* * +* Description : Initialise une instance d'objet à la structure dense. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_init(GThickObject *obj) +{ + obj->extra = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_dispose(GObject *object) +{ + G_OBJECT_CLASS(g_thick_object_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_finalize(GObject *object) +{ + G_OBJECT_CLASS(g_thick_object_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à manipuler. * +* * +* Description : Pose un verrou à l'aide du bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_thick_object_lock(GThickObject *obj) +{ + g_bit_lock((gint *)&obj->extra, GOBJECT_LOCK_BIT); + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à manipuler. * +* * +* Description : Retire un verrou via le bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_thick_object_unlock(GThickObject *obj) +{ + g_bit_unlock((gint *)&obj->extra, GOBJECT_LOCK_BIT); + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à manipuler. * +* * +* Description : Vérifie qu'un verrou est appliqué à l'aide du bit de GObject.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ +#ifndef NDEBUG +bool g_thick_object_check_lock(GThickObject *obj) +{ + bool result; /* Bilan à retourner */ + gboolean status; /* Bilan d'une tentative */ + + status = g_bit_trylock((gint *)&obj->extra, GOBJECT_LOCK_BIT); + + result = (status == FALSE); + + return result; + +} +#endif + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à consulter. * +* * +* Description : Fournit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : Valeur de 32 lues et expurgées des bits GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +guint g_thick_object_get_extra(const GThickObject *obj) +{ + guint result; /* Valeur à retourner */ + guint mask; /* Masque à appliquer */ + + result = g_atomic_int_get(&obj->extra); + + assert(GOBJECT_RESERVED_EXTRA_BITS < 30); + + mask = (1 << GOBJECT_RESERVED_EXTRA_BITS) - 1; + + result &= ~mask; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à consulter. * +* val = valeur de 32 à conserver. * +* * +* Description : Définit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_thick_object_set_extra(GThickObject *obj, guint val) +{ + guint mask; /* Masque à appliquer */ + + assert(GOBJECT_RESERVED_EXTRA_BITS < 30); + + mask = (1 << GOBJECT_RESERVED_EXTRA_BITS) - 1; + + assert((val & mask) == 0); + + val &= ~mask; + + g_atomic_int_and(&obj->extra, val | mask); + + g_atomic_int_or(&obj->extra, val); + +} diff --git a/src/glibext/objhole.h b/src/glibext/objhole.h index 38f4bd5..461c37a 100644 --- a/src/glibext/objhole.h +++ b/src/glibext/objhole.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * objhole.h - prototypes pour l'utilisation d'un espace inutilisé dans la structure GObject * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,118 +25,36 @@ #define _GLIBEXT_OBJHOLE_H -#include <glib.h> -#include <glib-object.h> - - -#include "../common/cpp.h" - - - -/** - * Une structure GObject a la définition suivante : - * - * struct _GObject - * { - * GTypeInstance g_type_instance; - * volatile guint ref_count; - * GData *qdata; - * }; - * - * En revanche, le fichier "glib/gobject/gobject.c" précise la définition - * réelle de la structure selon l'environnement : - * - * #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8 - * #define HAVE_OPTIONAL_FLAGS - * #endif - * - * typedef struct - * { - * GTypeInstance g_type_instance; - * guint ref_count; - * #ifdef HAVE_OPTIONAL_FLAGS - * guint optional_flags; - * #endif - * GData *qdata; - * } GObjectReal; - * - * G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal)); - * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count)); - * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata)); - * - * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois. - */ - -#if 0 - -# define GET_GOBJECT_EXTRA(obj, tp) \ - ({ \ - BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ - tp *___result; \ - ___result = (tp *)(((guint *)&obj->ref_count) + 1); \ - BUILD_BUG_ON((___result + 1) == (tp *)&obj->qdata); \ - ___result; \ - }) - +#ifndef NDEBUG +# include <stdbool.h> #endif -/** - * Choix du bit de verrou pour le champ "lock". - * - * Dans la structure exploitant le mot utilisé ici, ce verrou est généralement - * placé dans le bit de poids fort pour les objets qui l'utilisent. - */ +#include "../glibext/helpers.h" + -#if __BYTE_ORDER == __LITTLE_ENDIAN -# define HOLE_LOCK_BIT 31 +#define G_TYPE_THICK_OBJECT (g_thick_object_get_type()) -#elif __BYTE_ORDER == __BIG_ENDIAN +DECLARE_GTYPE(GThickObject, g_thick_object, G, THICK_OBJECT); -# define HOLE_LOCK_BIT 0 -#else +/* Pose un verrou à l'aide du bit dédié de GObject. */ +void g_thick_object_lock(GThickObject *); -# error "Unknown byte order" +/* Retire un verrou via le bit dédié de GObject. */ +void g_thick_object_unlock(GThickObject *); +/* Vérifie qu'un verrou est appliqué à l'aide du bit de GObject. */ +#ifndef NDEBUG +bool g_thick_object_check_lock(GThickObject *obj); #endif +/* Fournit la valeur courante de la zone de stockage d'un objet. */ +guint g_thick_object_get_extra(const GThickObject *); -/* Verrou d'accès pour une encapsulation */ -typedef struct _lockable_obj_extra_t -{ - gint lock; /* Gestion d'accès aux fanions */ - -} lockable_obj_extra_t; - - -#define INIT_GOBJECT_EXTRA_LOCK(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - __lockable->lock = 0; \ - } \ - while (0) - -#define LOCK_GOBJECT_EXTRA(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - g_bit_lock(&__lockable->lock, HOLE_LOCK_BIT); \ - } \ - while (0) - -#define UNLOCK_GOBJECT_EXTRA(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - g_bit_unlock(&__lockable->lock, HOLE_LOCK_BIT); \ - } \ - while (0) +/* Définit la valeur courante de la zone de stockage d'un objet. */ +void g_thick_object_set_extra(GThickObject *, guint); diff --git a/src/glibext/options/Makefile.am b/src/glibext/options/Makefile.am index 448de7b..3f400c6 100644 --- a/src/glibext/options/Makefile.am +++ b/src/glibext/options/Makefile.am @@ -2,7 +2,8 @@ noinst_LTLIBRARIES = libglibextoptions.la -libglibextoptions_la_SOURCES = \ +libglibextoptions_la_SOURCES = \ + asm.h \ hex.h hex.c libglibextoptions_la_CFLAGS = $(TOOLKIT_CFLAGS) diff --git a/src/glibext/options/asm.h b/src/glibext/options/asm.h new file mode 100644 index 0000000..d7a2c86 --- /dev/null +++ b/src/glibext/options/asm.h @@ -0,0 +1,65 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * asm.h - prototypes pour les options de rendus de code désassemblé + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_OPTIONS_ASM_H +#define _GLIBEXT_OPTIONS_ASM_H + + +#include "../helpers.h" + + + +/* Liste des colonnes en options */ +typedef enum _DisassColumnOptions +{ + ACO_PHYSICAL, /* Position physique */ + ACO_VIRTUAL, /* Adresse virtuelle */ + ACO_BINARY, /* Contenu sous forme binaire */ + + ACO_COUNT + +} DisassColumnOptions; + + +#define ACO_ASSEMBLY_LABEL (ACO_COUNT + 0) /* Etiquette dans les données */ +#define ACO_ASSEMBLY_HEAD (ACO_COUNT + 1) /* Instruction pour assembleur */ +#define ACO_ASSEMBLY (ACO_COUNT + 2) /* Code pour assembleur */ +#define ACO_COMMENTS (ACO_COUNT + 3) /* Commentaires éventuels */ + + + + + + +#if 0 +#define G_TYPE_HEX_OPTIONS (g_hex_options_get_type()) + +DECLARE_GTYPE(GHexOptions, g_hex_options, G, HEX_OPTIONS); + + +/* Crée un groupe d'options pour le rendu d'hexadécimal. */ +GHexOptions *g_hex_options_new(void); +#endif + + +#endif /* _GLIBEXT_OPTIONS_HEX_H */ diff --git a/src/glibext/gbinportion-int.h b/src/glibext/portion-int-ui.h index a29f53c..a29f53c 100644 --- a/src/glibext/gbinportion-int.h +++ b/src/glibext/portion-int-ui.h diff --git a/src/glibext/portion-int.h b/src/glibext/portion-int.h new file mode 100644 index 0000000..c044206 --- /dev/null +++ b/src/glibext/portion-int.h @@ -0,0 +1,69 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * portion-int.h - prototypes internes pour la définition interne des portions de binaire + * + * Copyright (C) 2019-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef GLIBEXT_PORTION_INT_H +#define GLIBEXT_PORTION_INT_H + + +#include "portion.h" + + + +/* Portion de données binaires quelconques (instance) */ +struct _GBinaryPortion +{ + GObject parent; /* A laisser en premier */ + + char *desc; /* Désignation humaine */ + + mrange_t range; /* Emplacement dans le code */ + bool continued; /* Suite d'une découpe ? */ + + PortionAccessRights rights; /* Droits d'accès */ + + GBinaryPortion **subs; /* Portions incluses */ + size_t count; /* Quantité d'inclusions */ + +}; + +/* Portion de données binaires quelconques (classe) */ +struct _GBinaryPortionClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une description de partie de code vierge. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_create(GBinaryPortion *, const vmpa2t *, phys_t); + + + +#endif /* GLIBEXT_PORTION_INT_H */ diff --git a/src/glibext/gbinportion.c b/src/glibext/portion-ui.c index 12e12fb..12e12fb 100644 --- a/src/glibext/gbinportion.c +++ b/src/glibext/portion-ui.c diff --git a/src/glibext/gbinportion.h b/src/glibext/portion-ui.h index ea4b4aa..ea4b4aa 100644 --- a/src/glibext/gbinportion.h +++ b/src/glibext/portion-ui.h diff --git a/src/glibext/portion.c b/src/glibext/portion.c new file mode 100644 index 0000000..096a39d --- /dev/null +++ b/src/glibext/portion.c @@ -0,0 +1,974 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * portion.c - représentation graphique de portions de binaire + * + * Copyright (C) 2013-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "portion.h" + + +#include <assert.h> +#include <malloc.h> +//#include <stdio.h> +//#include <stdlib.h> +#include <string.h> + + +#include <i18n.h> + + +#include "portion-int.h" +//#include "../analysis/human/asm/lang.h" +#include "../common/extstr.h" +#include "../common/sort.h" +//#include "../core/columns.h" +//#include "../glibext/gbinarycursor.h" +//#include "../glibext/linegen-int.h" + + + +/* ------------------------------- PORTION DE BINAIRE ------------------------------- */ + + +/* Initialise la classe des portions de données binaires. */ +static void g_binary_portion_class_init(GBinaryPortionClass *); + +/* Initialise une instance de portion de données binaires. */ +static void g_binary_portion_init(GBinaryPortion *); + +/* Supprime toutes les références externes. */ +static void g_binary_portion_dispose(GBinaryPortion *); + +/* Procède à la libération totale de la mémoire. */ +static void g_binary_portion_finalize(GBinaryPortion *); + + + +/* ------------------------ PARCOURS D'ENSEMBLES DE PORTIONS ------------------------ */ + + +/* Détermine si une portion contient une adresse donnée. */ +static bool g_binary_portion_contains_vmpa(const GBinaryPortion *, const vmpa2t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* PORTION DE BINAIRE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini par la GLib pour les portions de données binaires. */ +G_DEFINE_TYPE(GBinaryPortion, g_binary_portion, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des portions de données binaires. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_portion_class_init(GBinaryPortionClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_portion_dispose; + object->finalize = (GObjectFinalizeFunc)g_binary_portion_finalize; + + +} + + +/****************************************************************************** +* * +* Paramètres : portion = instance à initialiser. * +* * +* Description : Initialise une instance de portion de données binaires. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_portion_init(GBinaryPortion *portion) +{ + vmpa2t dummy; /* Coquille presque vide */ + + portion->desc = NULL; + + init_vmpa(&dummy, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + init_mrange(&portion->range, &dummy, VMPA_NO_VIRTUAL); + + portion->continued = false; + + portion->rights = PAC_NONE; + + portion->subs = NULL; + portion->count = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_portion_dispose(GBinaryPortion *portion) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < portion->count; i++) + g_clear_object(&portion->subs[i]); + + G_OBJECT_CLASS(g_binary_portion_parent_class)->dispose(G_OBJECT(portion)); + +} + + +/****************************************************************************** +* * +* Paramètres : portion = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_portion_finalize(GBinaryPortion *portion) +{ + if (portion->desc != NULL) + free(portion->desc); + + if (portion->subs != NULL) + free(portion->subs); + + G_OBJECT_CLASS(g_binary_portion_parent_class)->finalize(G_OBJECT(portion)); + +} + + +/****************************************************************************** +* * +* Paramètres : addr = emplacement de la section à conserver. * +* size = taille de la section à conserver. * +* * +* Description : Crée une description de partie de code vierge. * +* * +* Retour : Instance mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBinaryPortion *g_binary_portion_new(const vmpa2t *addr, phys_t size) +{ + GBinaryPortion *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_BINARY_PORTION, NULL); + + if (!g_binary_portion_create(result, addr, size)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = instance à initialiser pleinement. * +* addr = emplacement de la section à conserver. * +* size = taille de la section à conserver. * +* * +* Description : Met en place une description de partie de code vierge. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_create(GBinaryPortion *portion, const vmpa2t *addr, phys_t size) +{ + bool result; /* Bilan à retourner */ + + result = true; + + init_mrange(&portion->range, addr, size); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : a = premières informations à consulter. * +* b = secondes informations à consulter. * +* * +* Description : Etablit la comparaison ascendante entre deux portions. * +* * +* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). * +* * +* Remarques : - * +* * +******************************************************************************/ + +int g_binary_portion_compare(const GBinaryPortion **a, const GBinaryPortion **b) +{ + int result; /* Bilan à retourner */ + const vmpa2t *addr_a; /* Adresse de la portion 'a' */ + const vmpa2t *addr_b; /* Adresse de la portion 'b' */ + + addr_a = get_mrange_addr(&(*a)->range); + addr_b = get_mrange_addr(&(*b)->range); + + result = cmp_vmpa(addr_a, addr_b); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à mettre à jour. * +* desc = nom à donner à la partie. * +* * +* Description : Attribue une description humaine à une partie de code. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_binary_portion_set_desc(GBinaryPortion *portion, const char *desc) +{ + if (portion->desc != NULL) + free(portion->desc); + + if (desc == NULL) + portion->desc = NULL; + + else + { + portion->desc = strdup(desc); + + if (portion->continued) + portion->desc = stradd(portion->desc, _(" (continued)")); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à consulter. * +* * +* Description : Fournit la description attribuée à une partie de code. * +* * +* Retour : Nom donné à la partie. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_binary_portion_get_desc(const GBinaryPortion *portion) +{ + return portion->desc; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à mettre à jour. * +* * +* Description : Fournit l'emplacement d'une partie de code binaire. * +* * +* Retour : Espace de couverture associé à la portion. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const mrange_t *g_binary_portion_get_range(const GBinaryPortion *portion) +{ + return &portion->range; + +} + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à mettre à jour. * +* max = taille maximale accordée à la portion. * +* * +* Description : Assure qu'une portion ne dépasse pas une position donnée. * +* * +* Retour : true si la portion a été modifiée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_limit_range(GBinaryPortion *portion, phys_t max) +{ + bool result; /* Bilan à retourner */ + phys_t current; /* Taille courante */ + + current = get_mrange_length(&portion->range); + + result = (current > max); + + if (result) + set_mrange_length(&portion->range, max); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion dont la définition est à metre à jour. * +* * +* Description : Définit la nature de la portion en terme d'originalité. * +* * +* Retour : - * +* * +* Remarques : L'action ne modifie aucunement la description courante. * +* C'est le changement de description qui s'appuie sur la * +* notée ici. * +* * +******************************************************************************/ + +void g_binary_portion_mark_as_continued(GBinaryPortion *portion, bool continued) +{ + portion->continued = continued; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion dont la définition est à consulter. * +* * +* Description : Indique la nature de la portion en terme d'originalité. * +* * +* Retour : true si la portion est la suite d'une portion découpée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_is_continuation(const GBinaryPortion *portion) +{ + return portion->continued; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à mettre à jour. * +* rights = droits d'accès de la partie. * +* * +* Description : Définit les droits associés à une partie de code. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_binary_portion_set_rights(GBinaryPortion *portion, PortionAccessRights rights) +{ + portion->rights = rights; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à consulter. * +* * +* Description : Fournit les droits associés à une partie de code. * +* * +* Retour : Droits d'accès de la partie. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PortionAccessRights g_binary_portion_get_rights(const GBinaryPortion *portion) +{ + return portion->rights; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion principale à compléter. * +* sub = portion à inclure dans la définition courante. * +* * +* Description : Procède à l'inclusion d'une portion dans une autre. * +* * +* Retour : Bilan de l'opération : true si inclusion, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_include(GBinaryPortion *portion, GBinaryPortion *sub) +{ + bool result; /* Bilan à retourner */ + bool found; /* Zone d'accueil trouvée ? */ + size_t best; /* Meilleur point d'insertion */ + size_t missed; /* Indice de zone à déplacer */ + const mrange_t *brange; /* Raccourci de confort d'usage*/ + vmpa2t end; /* Fin de la zone commune */ + phys_t overlapping; /* Taille de la zone commune */ + bool continued; /* Suite d'une découpe ? */ + GBinaryPortion *left_part; /* Partie intégrable */ + vmpa2t start; /* Départ de la seconde partie */ + GBinaryPortion *right_part; /* Partie restante */ + + int g_binary_portion_is_included(const GBinaryPortion **a, const GBinaryPortion **b) + { + int result; /* Bilan à retourner */ + + result = mrange_includes_mrange(&(*b)->range, &(*a)->range); + + return result; + + } + + found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *), + (__compar_fn_t)g_binary_portion_is_included, &best); + + if (!found) + { + /** + * On se prépare à réaliser une insertion au niveau courant. Mais des + * portions précédentes sont peut-être à déplacer dans la nouvelle zone : + * + * EXIDX 0x001178 0x00009178 0x00009178 0x00008 0x00008 R 0x4 + * PHDR 0x000034 0x00008034 0x00008034 0x00120 0x00120 R E 0x4 + * INTERP 0x000154 0x00008154 0x00008154 0x00019 0x00019 R 0x1 + * LOAD 0x000000 0x00008000 0x00008000 0x01184 0x01184 R E 0x8000 + * + * On refait donc une passe sur toutes les sous-portions du niveau. + * + * Cette approche a le mérite de traiter également et naturellement les + * sections définies dans le désordre : + * + * [21] .bss NOBITS 00088240 07823c 0018c8 00 WA 0 0 8 + * [22] __libc_freeres_ptrs NOBITS 00089b08 07823c 000018 00 WA 0 0 4 + * [23] .comment PROGBITS 00000000 07823c 000022 01 MS 0 0 1 + * + * Quant aux cas de figure où les portions sont identiques, l'ordre d'appel + * induit l'ordre d'inclusion. + * + * Cela concerne par exemple les zones de données : + * + * En-têtes de section: + * [Nr] Nom Type Adr Décala.Taille ES Fan LN Inf Al + * ... + * [ 2] .data PROGBITS 00010098 000098 00000c 00 WA 0 0 1 + * + * En-têtes de programme: + * Type Décalage Adr. vir. Adr.phys. T.Fich. T.Mém. Fan Alignement + * ... + * LOAD 0x000098 0x00010098 0x00010098 0x0000c 0x0000c RW 0x8000 + * + */ + + int g_binary_portion_track_missed_inclusion(const GBinaryPortion **a, const GBinaryPortion **b) + { + int result; /* Bilan à retourner */ + + result = mrange_includes_mrange(&(*a)->range, &(*b)->range); + + return result; + + } + + do + { + found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *), + (__compar_fn_t)g_binary_portion_track_missed_inclusion, &missed); + + if (found) + { + result = g_binary_portion_include(sub, portion->subs[missed]); + assert(result); + + portion->subs = _qdelete(portion->subs, &portion->count, sizeof(GBinaryPortion *), missed); + + } + + } + while (found); + + /** + * Il peut arriver que certaines portions débordent de leur zone d'inclusion : + * + * [24] .bss NOBITS 00012088 002084 000044 00 WA 0 0 8 + * [25] .ARM.attributes ARM_ATTRIBUTES 00000000 002084 000037 00 0 0 1 + * [26] .shstrtab STRTAB 00000000 0020bb 0000ed 00 0 0 1 + * + * Afin de respecter une certaine cohérence dans l'arbre des portions, on choisit + * de découper la portion qui déborde. + */ + + int g_binary_portion_track_partial_inclusion(const GBinaryPortion **a, const GBinaryPortion **b) + { + int result; /* Bilan à retourner */ + + result = cmp_mrange_with_vmpa(&(*b)->range, get_mrange_addr(&(*a)->range)); + + return result; + + } + + found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *), + (__compar_fn_t)g_binary_portion_track_partial_inclusion, &best); + + if (found) + { + brange = &portion->subs[best]->range; + + compute_mrange_end_addr(brange, &end); + overlapping = compute_vmpa_diff(get_mrange_addr(&sub->range), &end); + + continued = g_binary_portion_is_continuation(sub); + + /* Partie contenue */ + + left_part = g_binary_portion_new(get_mrange_addr(&sub->range), overlapping); + + g_binary_portion_set_desc(left_part, sub->desc); + g_binary_portion_mark_as_continued(left_part, continued); + g_binary_portion_set_rights(left_part, sub->rights); + + /* Partie qui déborde... */ + + /** + * Comme la portion incluante peut avoir une définition d'adresse + * virtuelle différente de celle de la portion incluse, on recalcule + * la position de départ de la seconde partie de la portion découpée + * à partir des données d'origine. + */ + + copy_vmpa(&start, get_mrange_addr(&sub->range)); + advance_vmpa(&start, overlapping); + + right_part = g_binary_portion_new(&start, get_mrange_length(&sub->range) - overlapping); + + if (!continued) + g_binary_portion_mark_as_continued(right_part, true); + + g_binary_portion_set_desc(right_part, sub->desc); + + if (continued) + g_binary_portion_mark_as_continued(right_part, true); + + g_binary_portion_set_rights(right_part, sub->rights); + + /* Inclusions des parties */ + + result = g_binary_portion_include(portion, left_part); + + if (result) + result = g_binary_portion_include(portion, right_part); + + unref_object(left_part); + unref_object(right_part); + + } + + else + { + ref_object(sub); + + portion->subs = qinsert(portion->subs, &portion->count, sizeof(GBinaryPortion *), + (__compar_fn_t)g_binary_portion_compare, &sub); + + result = true; + + } + + } + + /* Poursuite de l'inclusion dans la sous-portion adaptée... */ + else + result = g_binary_portion_include(portion->subs[best], sub); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = première couche amorçant la visite. * +* visitor = fonction à appeler à chaque étape de la descente. * +* data = adresse pointant vers des données de l'utilisateur.* +* * +* Description : Parcourt un ensemble de portions binaires. * +* * +* Retour : true si la visite a été jusqu'à son terme, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_visit(GBinaryPortion *portion, visit_portion_fc visitor, void *data) +{ + bool result; /* Etat à retourner */ + + bool visit_portion(GBinaryPortion *p, GBinaryPortion *pp) + { + bool ret; /* Etat à retourner */ + size_t i; /* Boucle de parcours */ + + if (p->count == 0) + ret = visitor(p, pp, BPV_SHOW, data); + + else + { + ret = visitor(p, pp, BPV_ENTER, data); + + for (i = 0; i < p->count && ret; i++) + ret = visit_portion(p->subs[i], p); + + if (ret) + ret = visitor(p, pp, BPV_EXIT, data); + + } + + return ret; + + } + + result = visit_portion(portion, NULL); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* PARCOURS D'ENSEMBLES DE PORTIONS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : portion = portion mère à consulter. * +* addr = adresse du point de recherche. * +* * +* Description : Détermine si une portion contient une adresse donnée. * +* * +* Retour : true ou false selon le résultat. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_portion_contains_vmpa(const GBinaryPortion *portion, const vmpa2t *addr) +{ + bool result; /* Bilan à retourner */ + const mrange_t *range; /* Emplacement de portion */ + + result = false; + + range = g_binary_portion_get_range(portion); + + /* Portion non allouée en mémoire : on écarte */ + if (!has_virt_addr(get_mrange_addr(range))) + goto not_found; + + result = mrange_contains_addr(range, addr); + + not_found: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = couche de portions à parcourir pour les recherches.* +* addr = adresse du point de recherche. * +* * +* Description : Recherche la portion présente à une adresse donnée. * +* * +* Retour : Portion trouvée à l'endroit indiqué. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBinaryPortion *g_binary_portion_find_at_addr(GBinaryPortion *portion, const vmpa2t *addr) +{ + GBinaryPortion *result; /* Portion à retourner */ + size_t i; /* Boucle de parcours */ + GBinaryPortion *sub; /* Portion incluse à traiter */ + + result = NULL; + + if (!g_binary_portion_contains_vmpa(portion, addr)) + goto done; + + for (i = 0; i < portion->count && result == NULL; i++) + { + sub = portion->subs[i]; + + if (!g_binary_portion_contains_vmpa(sub, addr)) + continue; + + result = g_binary_portion_find_at_addr(sub, addr); + + } + + if (result == NULL) + { + result = portion; + ref_object(result); + } + + done: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion mère à consulter. * +* off = position physique du point de recherche. * +* * +* Description : Détermine si une portion contient une position donnée. * +* * +* Retour : true ou false selon le résultat. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_portion_contains_physical(const GBinaryPortion *portion, phys_t off) +{ + bool result; /* Bilan à retourner */ + const mrange_t *range; /* Emplacement de portion */ + const vmpa2t *addr; /* Départ de la portion */ + + range = g_binary_portion_get_range(portion); + addr = get_mrange_addr(range); + + if (!has_phys_addr(addr)) + result = false; + + else + result = (addr->physical <= off && off < (addr->physical + range->length)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = couche de portions à parcourir pour les recherches.* +* off = position physique à retrouver. * +* pos = position correspondante. [OUT] * +* * +* Description : Fournit l'emplacement correspondant à une position physique. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_translate_offset_into_vmpa(const GBinaryPortion *portion, phys_t off, vmpa2t *pos) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours #1 */ + GBinaryPortion *sub; /* Portion incluse à traiter */ + const mrange_t *range; /* Emplacement de portion */ + const vmpa2t *addr; /* Départ de la portion */ + + result = false; + + for (i = 0; i < portion->count; i++) + { + sub = portion->subs[i]; + + if (!g_binary_portion_contains_physical(sub, off)) + continue; + + result = g_binary_portion_translate_offset_into_vmpa(sub, off, pos); + + break; + + } + + if (i == portion->count) + { + result = g_binary_portion_contains_physical(portion, off); + + if (result) + { + range = g_binary_portion_get_range(portion); + addr = get_mrange_addr(range); + + if (has_virt_addr(get_mrange_addr(range))) + init_vmpa(pos, off, addr->virtual + off - addr->physical); + + else + init_vmpa(pos, off, VMPA_NO_VIRTUAL); + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion mère à consulter. * +* virt = adresse virtuelle du point de recherche. * +* * +* Description : Détermine si une portion contient une adresse donnée. * +* * +* Retour : true ou false selon le résultat. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_portion_contains_virtual(const GBinaryPortion *portion, virt_t virt) +{ + bool result; /* Bilan à retourner */ + const mrange_t *range; /* Emplacement de portion */ + const vmpa2t *addr; /* Départ de la portion */ + + range = g_binary_portion_get_range(portion); + addr = get_mrange_addr(range); + + if (!has_virt_addr(addr)) + result = false; + + else + result = (addr->virtual <= virt && virt < (addr->virtual + range->length)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = couche de portions à parcourir pour les recherches.* +* virt = adresse virtuelle à retrouver. * +* pos = position correspondante. [OUT] * +* * +* Description : Fournit l'emplacement correspondant à une adresse virtuelle. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_translate_address_into_vmpa(const GBinaryPortion *portion, virt_t virt, vmpa2t *pos) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours #1 */ + GBinaryPortion *sub; /* Portion incluse à traiter */ + const mrange_t *range; /* Emplacement de portion */ + const vmpa2t *addr; /* Départ de la portion */ + + result = false; + + for (i = 0; i < portion->count; i++) + { + sub = portion->subs[i]; + + if (!g_binary_portion_contains_virtual(sub, virt)) + continue; + + result = g_binary_portion_translate_address_into_vmpa(sub, virt, pos); + + break; + + } + + if (i == portion->count) + { + result = g_binary_portion_contains_virtual(portion, virt); + + if (result) + { + range = g_binary_portion_get_range(portion); + addr = get_mrange_addr(range); + + if (has_phys_addr(addr) && has_virt_addr(addr)) + init_vmpa(pos, addr->physical + virt - addr->virtual, virt); + + else + init_vmpa(pos, VMPA_NO_PHYSICAL, virt); + + } + + } + + return result; + +} diff --git a/src/glibext/portion.h b/src/glibext/portion.h new file mode 100644 index 0000000..88d69b6 --- /dev/null +++ b/src/glibext/portion.h @@ -0,0 +1,121 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * portion.h - prototypes pour la représentation graphique de portions de binaire + * + * Copyright (C) 2013-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_PORTION_H +#define _GLIBEXT_PORTION_H + + +#include <stdbool.h> + + +#include "helpers.h" +#include "../arch/vmpa.h" + + + +/* ------------------------------- PORTION DE BINAIRE ------------------------------- */ + + +#define G_TYPE_BINARY_PORTION (g_binary_portion_get_type()) + +DECLARE_GTYPE(GBinaryPortion, g_binary_portion, G, BINARY_PORTION); + + +/* Crée une description de partie de code vierge. */ +GBinaryPortion *g_binary_portion_new(const vmpa2t *, phys_t); + +/* Etablit la comparaison ascendante entre deux portions. */ +int g_binary_portion_compare(const GBinaryPortion **, const GBinaryPortion **); + +/* Attribue une description humaine à une partie de code. */ +void g_binary_portion_set_desc(GBinaryPortion *, const char *); + +/* Fournit la description attribuée à une partie de code. */ +const char *g_binary_portion_get_desc(const GBinaryPortion *); + +/* Fournit l'emplacement d'une partie de code binaire. */ +const mrange_t *g_binary_portion_get_range(const GBinaryPortion *); + +/* Assure qu'une portion ne dépasse pas une position donnée. */ +bool g_binary_portion_limit_range(GBinaryPortion *, phys_t); + +/* Définit la nature de la portion en terme d'originalité. */ +void g_binary_portion_mark_as_continued(GBinaryPortion *, bool); + +/* Indique la nature de la portion en terme d'originalité. */ +bool g_binary_portion_is_continuation(const GBinaryPortion *); + +/* Droits d'accès à une portion */ +typedef enum _PortionAccessRights +{ + PAC_NONE = (0 << 0), /* Aucun */ + PAC_READ = (1 << 0), /* Lecture */ + PAC_WRITE = (1 << 1), /* Ecriture */ + PAC_EXEC = (1 << 2) /* Exécution */ + +} PortionAccessRights; + +#define PAC_ALL ((PortionAccessRights)(PAC_READ | PAC_WRITE | PAC_EXEC)) + +/* Définit les droits associés à une partie de code. */ +void g_binary_portion_set_rights(GBinaryPortion *, PortionAccessRights); + +/* Fournit les droits associés à une partie de code. */ +PortionAccessRights g_binary_portion_get_rights(const GBinaryPortion *); + +/* Procède à l'inclusion d'une portion dans une autre. */ +bool g_binary_portion_include(GBinaryPortion *, GBinaryPortion *); + +/* Sens des visites */ +typedef enum _BinaryPortionVisit +{ + BPV_ENTER, /* Arrivée sur une branche */ + BPV_SHOW, /* Visite d'une feuille */ + BPV_EXIT /* Départ d'une branche */ + +} BinaryPortionVisit; + + +/* Fonction appelée à chaque visite de portion.*/ +typedef bool (* visit_portion_fc) (GBinaryPortion *, GBinaryPortion *, BinaryPortionVisit, void *); + +/* Parcourt un ensemble de portions binaires. */ +bool g_binary_portion_visit(GBinaryPortion *, visit_portion_fc, void *); + + + +/* ------------------------ PARCOURS D'ENSEMBLES DE PORTIONS ------------------------ */ + + +/* Recherche la portion présente à une adresse donnée. */ +GBinaryPortion *g_binary_portion_find_at_addr(GBinaryPortion *, const vmpa2t *); + +/* Fournit l'emplacement correspondant à une position physique. */ +bool g_binary_portion_translate_offset_into_vmpa(const GBinaryPortion *, phys_t, vmpa2t *); + +/* Fournit l'emplacement correspondant à une adresse virtuelle. */ +bool g_binary_portion_translate_address_into_vmpa(const GBinaryPortion *, virt_t, vmpa2t *); + + + +#endif /* _GLIBEXT_BINPORTION_H */ diff --git a/src/glibext/secstorage-int.h b/src/glibext/secstorage-int.h new file mode 100644 index 0000000..bbac133 --- /dev/null +++ b/src/glibext/secstorage-int.h @@ -0,0 +1,60 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage-int.h - définitions internes pour la conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_SECSTORAGE_INT_H +#define _GLIBEXT_SECSTORAGE_INT_H + + +#include "secstorage.h" + + + +/* Gardien des secrets avec support des stockages (instance) */ +struct _GSecretStorage +{ + GObject parent; /* A laisser en premier */ + + GSettings *settings; /* Configuration sollicitée */ + + void *master_key; /* Clef déverrouillée */ + +}; + +/* Gardien des secrets avec support des stockages (classe) */ +struct _GSecretStorageClass +{ + GObjectClass parent; /* A laisser en premier */ + + /* Signaux */ + + void (* lock_update) (GSecretStorage *); + +}; + + +/* Met en place un gardien des secrets avec support de stockage. */ +bool g_secret_storage_create(GSecretStorage *, GSettings *); + + + +#endif /* _GLIBEXT_SECSTORAGE_INT_H */ diff --git a/src/glibext/secstorage.c b/src/glibext/secstorage.c new file mode 100644 index 0000000..161214c --- /dev/null +++ b/src/glibext/secstorage.c @@ -0,0 +1,950 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "secstorage.h" + + +#include <assert.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/rand.h> + + +#include "secstorage-int.h" +#include "../core/logs.h" + + + +/** + * Les mécanismes de hachage de mot de passe doivent être utilisés avec un sel, + * et la longueur du sel doit être d’au moins 128 bits. + * + * Cette note concerne le hachage de mots de passe et non la dérivation de secrets + * cependant. + * + * Source : https://cyber.gouv.fr/sites/default/files/2021/03/anssi-guide-selection_crypto-1.0.pdf + */ + +#define SECRET_STORAGE_SALT_SIZE (256 / 8) + + +/** + * Nombre d'itérations pour PBKDF2 : en 2023, OWASP recommande 600000 itérations + * pour PBKDF2-HMAC-SHA256 (et 210000 pour PBKDF2-HMAC-SHA512). + * + * Source : https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 + */ + +#define PBKDF2_HMAC_SHA256_ITERATIONS (2 << 20) + + +/** + * AES 256 : clef de 256 bits, IV de 128 bits + */ + +#define SECRET_STORAGE_KEK_SIZE (256 / 8) + +#define SECRET_STORAGE_KEY_SIZE (256 / 8) + +#define SECRET_STORAGE_BLOCK_SIZE (128 / 8) + +#define SECRET_STORAGE_IV_SIZE SECRET_STORAGE_BLOCK_SIZE + + + +/* Initialise la classe des stockages de secrets. */ +static void g_secret_storage_class_init(GSecretStorageClass *); + +/* Initialise une instance de stockage de secrets. */ +static void g_secret_storage_init(GSecretStorage *); + +/* Supprime toutes les références externes. */ +static void g_secret_storage_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_secret_storage_finalize(GObject *); + +/* Teste un mot de passe par Déverrouillage de clef maître. */ +static bool g_secret_storage_check_primary_password(GSecretStorage *, const char *, void **); + + + +/* Indique le type défini pour un gardien des secrets avec support des stockages. */ +G_DEFINE_TYPE(GSecretStorage, g_secret_storage, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des stockages de secrets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_class_init(GSecretStorageClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_secret_storage_dispose; + object->finalize = g_secret_storage_finalize; + + g_signal_new("lock-update", + G_TYPE_SECRET_STORAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GSecretStorageClass, lock_update), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance à initialiser. * +* * +* Description : Initialise une instance de stockage de secrets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_init(GSecretStorage *storage) +{ + storage->settings = NULL; + + storage->master_key = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_dispose(GObject *object) +{ + GSecretStorage *storage; /* Version spécialisée */ + + storage = G_SECRET_STORAGE(object); + + g_clear_object(&storage->settings); + + G_OBJECT_CLASS(g_secret_storage_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_finalize(GObject *object) +{ + GSecretStorage *storage; /* Version spécialisée */ + + storage = G_SECRET_STORAGE(object); + + if (storage->master_key != NULL) + free(storage->master_key); + + G_OBJECT_CLASS(g_secret_storage_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : settings = éventuel espace de configuration à utiliser. * +* * +* Description : Créé un nouveau gardien des secrets avec support de stockage.* +* * +* Retour : Gestionnaire de stockage sécurisé mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSecretStorage *g_secret_storage_new(GSettings *settings) +{ + GSecretStorage *result; /* Instance à retourner */ + + result = g_object_new(G_TYPE_SECRET_STORAGE, NULL); + + if (!g_secret_storage_create(result, settings)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = stockage sécurisé à initialiser. * +* settings = éventuel espace de configuration à utiliser. * +* * +* Description : Met en place un gardien des secrets avec support de stockage.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_create(GSecretStorage *storage, GSettings *settings) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (settings != NULL) + { + ref_object(settings); + storage->settings = settings; + } + else + storage->settings = g_settings_new("re.chrysalide.framework.secstorage"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_has_key(const GSecretStorage *storage) +{ + bool result; /* Bilan à retourner */ + GVariant *value; /* Valeur de configuration */ + gsize length; /* Taille d'une valeur donnée */ + + result = false; + + value = g_settings_get_value(storage->settings, "master"); + + g_variant_get_fixed_array(value, &length, 1); + + result = (length > SECRET_STORAGE_IV_SIZE); + + g_variant_unref(value); + + return result;; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* password = mot de passe principal à appliquer. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_set_password(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + unsigned char salt[SECRET_STORAGE_SALT_SIZE]; /* Sel pour la dérivation*/ + int ret; /* Bilan à d'un appel */ + GVariant *value; /* Valeur de configuration */ + unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ + unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + unsigned char encrypted[64]; /* Partie chiffrée à conserver */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_has_key(storage)) + goto exit; + + /* Création d'un sel pour la dérivation du mot de passe */ + + ret = RAND_bytes(salt, sizeof(salt)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /** + * La fonction g_variant_new_fixed_array() retourne un variant + * avec un décompte de référence flottant. + */ + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + salt, SECRET_STORAGE_SALT_SIZE, sizeof(unsigned char)); + + /** + * Comme le variant à une référence flottante, la fonction + * g_settings_set_value() consomme cette référence. + * + * Il n'y a donc pas lieu d'appeler g_variant_unref(). + */ + + g_settings_set_value(storage->settings, "salt", value); + + /* Dérivation du mot de passe */ + + ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), + salt, SECRET_STORAGE_SALT_SIZE, + PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), + SECRET_STORAGE_KEK_SIZE, kek); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Définition de la clef maître et de son IV de chiffrement */ + + ret = RAND_bytes(key, sizeof(key)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = RAND_bytes(iv, sizeof(iv)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Chiffrement de la clef maître */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + memcpy(encrypted, iv, SECRET_STORAGE_IV_SIZE); + + iter = encrypted + SECRET_STORAGE_IV_SIZE; + + ret = EVP_EncryptUpdate(ctx, iter, &outlen, key, SECRET_STORAGE_KEY_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - encrypted) < 64); + + /* Conservation de la clef protégée */ + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + encrypted, iter - encrypted, sizeof(unsigned char)); + + g_settings_set_value(storage->settings, "master", value); + + g_signal_emit_by_name(storage, "lock-update"); + + result = true; + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* old = ancien mot de passe principal à vérifier. * +* new = nouveau mot de passe principal à appliquer. * +* * +* Description : Modifie le mot de passe protégeant une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_change_password(GSecretStorage *storage, const char *old, const char *new) +{ + bool result; /* Bilan à retourner */ + + result = false; + + if (!g_secret_storage_has_key(storage)) + goto exit; + + if (!g_secret_storage_check_primary_password(storage, old, NULL)) + goto exit; + + if (!g_secret_storage_is_locked(storage)) + g_secret_storage_lock(storage); + + g_settings_reset(storage->settings, "salt"); + g_settings_reset(storage->settings, "master"); + + result = g_secret_storage_set_password(storage, new); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* password = mot de passe principal à appliquer. * +* * +* Description : Supprime le mot de passe protégeant une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_remove_password(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + + result = false; + + if (!g_secret_storage_has_key(storage)) + goto exit; + + if (!g_secret_storage_check_primary_password(storage, passwd, NULL)) + goto exit; + + if (!g_secret_storage_is_locked(storage)) + g_secret_storage_lock(storage); + + g_settings_reset(storage->settings, "salt"); + g_settings_reset(storage->settings, "master"); + + g_signal_emit_by_name(storage, "lock-update"); + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_is_locked(const GSecretStorage *storage) +{ + bool result; /* Bilan à retourner */ + + result = (storage->master_key == NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* password = mot de passe principal à utiliser. * +* master = éventuelle adresse pour un stockage de clef. [OUT]* +* * +* Description : Teste un mot de passe par Déverrouillage de clef maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_secret_storage_check_primary_password(GSecretStorage *storage, const char *passwd, void **master) +{ + bool result; /* Bilan à retourner */ + GVariant *salt_value; /* Valeur du sel configuré */ + gsize salt_length; /* Taille du sel conservé */ + gconstpointer salt; /* Données associées #1 */ + unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ + int ret; /* Bilan à d'un appel */ + GVariant *enc_value; /* Paramètres de chiffrement */ + gsize enc_length; /* Taille de ces paramètrs */ + gconstpointer encrypted; /* Données associées #2 */ + EVP_CIPHER_CTX *ctx; /* Contexte de déchiffrement */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (!g_secret_storage_is_locked(storage)) + { + result = true; + goto quick_exit; + } + + /* Récupération du sel mis en place */ + + salt_value = g_settings_get_value(storage->settings, "salt"); + + salt = g_variant_get_fixed_array(salt_value, &salt_length, sizeof(bin_t)); + + if (salt_length != SECRET_STORAGE_SALT_SIZE) + goto exit_with_salt; + + /* Dérivation du mot de passe */ + + ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), + salt, SECRET_STORAGE_SALT_SIZE, + PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), + SECRET_STORAGE_KEK_SIZE, kek); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_salt; + } + + /* Récupération des paramètres chiffrés */ + + enc_value = g_settings_get_value(storage->settings, "master"); + + encrypted = g_variant_get_fixed_array(enc_value, &enc_length, sizeof(bin_t)); + + if (enc_length <= SECRET_STORAGE_IV_SIZE) + goto exit_with_enc; + + /* Déhiffrement de la clef maître */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit_with_enc; + } + + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + memcpy(iv, encrypted, SECRET_STORAGE_IV_SIZE); + + ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter = key; + + ret = EVP_DecryptUpdate(ctx, iter, &outlen, + ((unsigned char *)encrypted) + SECRET_STORAGE_IV_SIZE, + enc_length - SECRET_STORAGE_IV_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + assert((iter - key) == SECRET_STORAGE_KEY_SIZE); + + /* Stockage de la clef maître en mémoire */ + + if (master != NULL) + { + *master = malloc(SECRET_STORAGE_KEY_SIZE); + memcpy(*master, key, SECRET_STORAGE_KEY_SIZE); + } + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + exit_with_enc: + + g_variant_unref(enc_value); + + exit_with_salt: + + g_variant_unref(salt_value); + + quick_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* password = mot de passe principal à utiliser. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_unlock(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + + result = g_secret_storage_check_primary_password(storage, passwd, &storage->master_key); + + if (result) + g_signal_emit_by_name(storage, "lock-update"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_secret_storage_lock(GSecretStorage *storage) +{ + if (storage->master_key != NULL) + { + free(storage->master_key); + storage->master_key = NULL; + + g_signal_emit_by_name(storage, "lock-update"); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* in = séquence d'octets à traiter. * +* out = séquence d'octets résultantes. [OUT] * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_encrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + int ret; /* Bilan à d'un appel */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + size_t needed; /* Taille de la sortie */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_is_locked(storage)) + goto quick_exit; + + /* Récupération de la clef maître et d'un IV de chiffrement */ + + ret = RAND_bytes(iv, sizeof(iv)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Préparation de la zone de réception */ + + needed = SECRET_STORAGE_IV_SIZE + ((in->size / SECRET_STORAGE_BLOCK_SIZE) + 1) * SECRET_STORAGE_BLOCK_SIZE; + + setup_sized_binary(out, needed); + + /* Chiffrement des données */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + memcpy(out->data, iv, SECRET_STORAGE_IV_SIZE); + + iter = out->bin_data + SECRET_STORAGE_IV_SIZE; + + ret = EVP_EncryptUpdate(ctx, iter, &outlen, in->bin_data, in->size); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - out->bin_data) == out->size); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + if (!result) + exit_sized_binary(out); + + exit: + quick_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* in = séquence d'octets à traiter. * +* out = séquence d'octets résultantes. [OUT] * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_decrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + int ret; /* Bilan à d'un appel */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + size_t needed; /* Taille de la sortie */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_is_locked(storage)) + goto quick_exit; + + /* Récupération d'un IV de déchiffrement */ + + if (in->size < SECRET_STORAGE_IV_SIZE) + goto exit; + + memcpy(iv, in->data, SECRET_STORAGE_IV_SIZE); + + /* Préparation de la zone de réception */ + + needed = in->size - SECRET_STORAGE_IV_SIZE; + + setup_sized_binary(out, needed); + + /* Chiffrement des données */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter = out->bin_data; + + ret = EVP_DecryptUpdate(ctx, iter, &outlen, + in->bin_data + SECRET_STORAGE_IV_SIZE, in->size - SECRET_STORAGE_IV_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - out->bin_data) <= out->size); + + resize_sized_binary(out, iter - out->bin_data); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + if (!result) + exit_sized_binary(out); + + exit: + quick_exit: + + return result; + +} diff --git a/src/glibext/secstorage.h b/src/glibext/secstorage.h new file mode 100644 index 0000000..ed3f79c --- /dev/null +++ b/src/glibext/secstorage.h @@ -0,0 +1,74 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour la conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_SECSTORAGE_H +#define _GLIBEXT_SECSTORAGE_H + + +#include <stdbool.h> +#include <gio/gio.h> + + +#include "helpers.h" +#include "../common/szbin.h" + + + +#define G_TYPE_SECRET_STORAGE (g_secret_storage_get_type()) + +DECLARE_GTYPE(GSecretStorage, g_secret_storage, G, SECRET_STORAGE); + + +/* Créé un nouveau gardien des secrets avec support de stockage. */ +GSecretStorage *g_secret_storage_new(GSettings *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +bool g_secret_storage_has_key(const GSecretStorage *); + +/* Définit un mot de passe pour protéger une clef maître. */ +bool g_secret_storage_set_password(GSecretStorage *, const char *); + +/* Modifie le mot de passe protégeant une clef maître. */ +bool g_secret_storage_change_password(GSecretStorage *, const char *, const char *); + +/* Supprime le mot de passe protégeant une clef maître. */ +bool g_secret_storage_remove_password(GSecretStorage *, const char *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +bool g_secret_storage_is_locked(const GSecretStorage *); + +/* Déverrouille la clef de chiffrement maître. */ +bool g_secret_storage_unlock(GSecretStorage *, const char *); + +/* Verrouille la clef de chiffrement maître. */ +void g_secret_storage_lock(GSecretStorage *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +bool g_secret_storage_encrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +bool g_secret_storage_decrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *); + + + +#endif /* _GLIBEXT_SECSTORAGE_H */ diff --git a/src/analysis/storage/serialize-int.h b/src/glibext/serialize-int.h index de8d3e3..df9c597 100644 --- a/src/analysis/storage/serialize-int.h +++ b/src/glibext/serialize-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * serialize-int.h - définitions internes propres aux objets entreposables dans un cache * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,8 +21,8 @@ */ -#ifndef _ANALYSIS_STORAGE_SERIALIZE_INT_H -#define _ANALYSIS_STORAGE_SERIALIZE_INT_H +#ifndef _GLIBEXT_SERIALIZE_INT_H +#define _GLIBEXT_SERIALIZE_INT_H #include "serialize.h" @@ -33,14 +33,14 @@ /* Charge un objet depuis une mémoire tampon. */ -typedef bool (* load_serializable_object_cb) (GSerializableObject *, GObjectStorage *, packed_buffer_t *); +typedef bool (* load_serializable_object_cb) (GSerializableObject *, GObjectStorage *, int); /* Sauvegarde un objet dans une mémoire tampon. */ -typedef bool (* store_serializable_object_cb) (const GSerializableObject *, GObjectStorage *, packed_buffer_t *); +typedef bool (* store_serializable_object_cb) (const GSerializableObject *, GObjectStorage *, int); /* Intermédiaire pour la mise en cache d'objet (interface) */ -struct _GSerializableObjectIface +struct _GSerializableObjectInterface { GTypeInterface base_iface; /* A laisser en premier */ @@ -50,9 +50,5 @@ struct _GSerializableObjectIface }; -/* Redéfinition */ -typedef GSerializableObjectIface GSerializableObjectInterface; - - -#endif /* _ANALYSIS_STORAGE_SERIALIZE_INT_H */ +#endif /* _GLIBEXT_SERIALIZE_INT_H */ diff --git a/src/analysis/storage/serialize.c b/src/glibext/serialize.c index d1b0502..b43f0c2 100644 --- a/src/analysis/storage/serialize.c +++ b/src/glibext/serialize.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * serialize.h - objets entreposables dans un cache * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -51,6 +51,8 @@ G_DEFINE_INTERFACE(GSerializableObject, g_serializable_object, G_TYPE_OBJECT) static void g_serializable_object_default_init(GSerializableObjectInterface *iface) { + iface->load = NULL; + iface->store = NULL; } @@ -58,10 +60,10 @@ static void g_serializable_object_default_init(GSerializableObjectInterface *ifa /****************************************************************************** * * * Paramètres : object = élément GLib à constuire. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à lire. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * * * -* Description : Charge un objet depuis une mémoire tampon. * +* Description : Charge un objet depuis un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -69,14 +71,14 @@ static void g_serializable_object_default_init(GSerializableObjectInterface *ifa * * ******************************************************************************/ -bool g_serializable_object_load(GSerializableObject *object, GObjectStorage *storage, packed_buffer_t *pbuf) +bool g_serializable_object_load(GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ - GSerializableObjectIface *iface; /* Interface utilisée */ + GSerializableObjectInterface *iface; /* Interface utilisée */ iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); - result = iface->load(object, storage, pbuf); + result = iface->load(object, storage, fd); return result; @@ -86,10 +88,10 @@ bool g_serializable_object_load(GSerializableObject *object, GObjectStorage *sto /****************************************************************************** * * * Paramètres : object = élément GLib à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * * * -* Description : Sauvegarde un objet dans une mémoire tampon. * +* Description : Sauvegarde un objet dans un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -97,14 +99,14 @@ bool g_serializable_object_load(GSerializableObject *object, GObjectStorage *sto * * ******************************************************************************/ -bool g_serializable_object_store(const GSerializableObject *object, GObjectStorage *storage, packed_buffer_t *pbuf) +bool g_serializable_object_store(const GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ - GSerializableObjectIface *iface; /* Interface utilisée */ + GSerializableObjectInterface *iface; /* Interface utilisée */ iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); - result = iface->store(object, storage, pbuf); + result = iface->store(object, storage, fd); return result; diff --git a/src/glibext/serialize.h b/src/glibext/serialize.h new file mode 100644 index 0000000..c95ac30 --- /dev/null +++ b/src/glibext/serialize.h @@ -0,0 +1,52 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * serialize.h - prototypes pour les objets entreposables dans un cache + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_SERIALIZE_H +#define _GLIBEXT_SERIALIZE_H + + +#include <stdbool.h> + + +#include "helpers.h" + + + +#define G_TYPE_SERIALIZABLE_OBJECT (g_serializable_object_get_type()) + +DECLARE_INTERFACE(GSerializableObject, g_serializable_object, G, SERIALIZABLE_OBJECT); + + +/* storage.h : définition d'une conservation d'objets construits */ +typedef struct _GObjectStorage GObjectStorage; + + +/* Charge un objet depuis un flux de données. */ +bool g_serializable_object_load(GSerializableObject *, GObjectStorage *, int); + +/* Sauvegarde un objet dans un flux de données. */ +bool g_serializable_object_store(const GSerializableObject *, GObjectStorage *, int); + + + +#endif /* _GLIBEXT_SERIALIZE_H */ diff --git a/src/glibext/signal.c b/src/glibext/sigredir.c index 33290fb..67e8563 100644 --- a/src/glibext/signal.c +++ b/src/glibext/sigredir.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * signal.c - encadrement des signaux supplémentaire par rapport à celui de la GLib + * sigredir.c - encadrement des signaux supplémentaire par rapport à celui de la GLib * - * Copyright (C) 2014-2018 Cyrille Bagard + * Copyright (C) 2014-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,49 +21,55 @@ */ -#include "signal.h" +#include "sigredir.h" #include <assert.h> #include <malloc.h> #include <stdarg.h> #include <stdbool.h> +#include <gobject/gclosure.h> #include <gobject/gvaluecollector.h> -/* Prototype pour le transfert d'exécution. */ -typedef void (* GSignalCallback) (gpointer, ...); +/* Informations utiles à l'appel final */ +typedef struct _gsignal_wrapper_params_t +{ + GClosure *closure; /* Glue pour les appels */ + + guint n_params; /* Nombre de paramètres */ + GValue instance_and_params[0]; /* Instance & paramètres */ + +} gsignal_wrapper_params_t; + +/* Transmet un signal dans le contexte principal. */ +static gboolean to_main_wrapper(gpointer); +/* Supprime de la mémoire le transporteur d'informations. */ +static void destroy_wrapper_params(gpointer); /* Informations concernant une diffusion de signal */ -typedef struct _gsignal_wrapper_info +typedef struct _gsignal_wrapper_info_t { gpointer instance; /* Instance GLib initiatrice */ + gulong id; /* Identifiant de connexion */ GClosure *closure; /* Glue pour les appels */ - GType return_type; /* Type de la valeur retournée */ guint n_params; /* Nombre de paramètres */ - const GType *param_types; /* Type des paramètres associés*/ + GType param_types[0]; /* Type des paramètres associés*/ - GValue return_value; /* Valeur de retour */ - GValue instance_and_params[0]; /* Instance & paramètres */ - -} gsignal_wrapper_info; - - -/* Transmet un signal dans le contexte principal. */ -static gboolean to_main_wrapper(gsignal_wrapper_info *); +} gsignal_wrapper_info_t; /* Réceptionne un signal et redirige son exécution. */ -static void carry_signal_to_main_thread(gsignal_wrapper_info *, ...); +static void carry_signal_to_main_thread(gsignal_wrapper_info_t *, ...); /****************************************************************************** * * -* Paramètres : info = collecteur d'informations sur la diffusion. * +* Paramètres : data = collecteur d'informations sur la diffusion. * * * * Description : Transmet un signal dans le contexte principal. * * * @@ -73,11 +79,17 @@ static void carry_signal_to_main_thread(gsignal_wrapper_info *, ...); * * ******************************************************************************/ -static gboolean to_main_wrapper(gsignal_wrapper_info *info) +static gboolean to_main_wrapper(gpointer data) { - g_closure_invoke(info->closure, NULL/*&info->return_value*/, - info->n_params + 1, info->instance_and_params, - NULL); + gsignal_wrapper_params_t *params; /* Informations d'appel */ + + params = (gsignal_wrapper_params_t *)data; + + g_closure_invoke(params->closure, + NULL /* return_value */, + params->n_params + 1, + params->instance_and_params, + NULL /* invocation_hint */); return G_SOURCE_REMOVE; @@ -86,6 +98,35 @@ static gboolean to_main_wrapper(gsignal_wrapper_info *info) /****************************************************************************** * * +* Paramètres : data = collecteur d'informations à supprimer. * +* * +* Description : Supprime de la mémoire le transporteur d'informations. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void destroy_wrapper_params(gpointer data) +{ + gsignal_wrapper_params_t *params; /* Informations d'appel */ + guint i; /* Boucle de parcours */ + + params = (gsignal_wrapper_params_t *)data; + + g_closure_unref(params->closure); + + for (i = 0; i < (params->n_params + 1); i++) + g_value_unset(params->instance_and_params + i); + + free(params); + +} + + +/****************************************************************************** +* * * Paramètres : info = collecteur d'informations sur la diffusion. * * ... = arguments poussés par la GLib sur la pile. * * * @@ -97,23 +138,26 @@ static gboolean to_main_wrapper(gsignal_wrapper_info *info) * * ******************************************************************************/ -static void carry_signal_to_main_thread(gsignal_wrapper_info *info, ...) +static void carry_signal_to_main_thread(gsignal_wrapper_info_t *info, ...) { + gsignal_wrapper_params_t *params; /* Informations d'appel */ GValue *param_values; /* Paramètres d'appel */ va_list ap; /* Liste d'arguments sur pile */ guint i; /* Boucle de parcours */ bool static_scope; /* Portée des arguments */ gchar *error; /* Eventuelle erreur inattendue*/ - //g_value_init(&info->return_value, info->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE); + params = calloc(1, sizeof(gsignal_wrapper_params_t) + sizeof(GValue) * (info->n_params + 1)); + + params->closure = info->closure; + g_closure_ref(info->closure); - if (G_IS_VALUE(info->instance_and_params)) - g_value_unset(info->instance_and_params); + params->n_params = info->n_params; - g_value_init(info->instance_and_params, G_TYPE_FROM_INSTANCE(info->instance)); - g_value_set_instance(info->instance_and_params, info->instance); + g_value_init(params->instance_and_params, G_TYPE_FROM_INSTANCE(info->instance)); + g_value_set_instance(params->instance_and_params, info->instance); - param_values = info->instance_and_params + 1; + param_values = params->instance_and_params + 1; va_start(ap, info); @@ -121,9 +165,6 @@ static void carry_signal_to_main_thread(gsignal_wrapper_info *info, ...) for (i = 0; i < info->n_params; i++) { - if (G_IS_VALUE(param_values + i)) - g_value_unset(param_values + i); - static_scope = info->param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE; G_VALUE_COLLECT_INIT(param_values + i, @@ -143,7 +184,28 @@ static void carry_signal_to_main_thread(gsignal_wrapper_info *info, ...) va_end(ap); if (error == NULL) - g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)to_main_wrapper, info, NULL); + g_idle_add_full(G_PRIORITY_HIGH_IDLE, to_main_wrapper, params, destroy_wrapper_params); + +} + + +/****************************************************************************** +* * +* Paramètres : info = collecteur d'informations à supprimer. * +* * +* Description : Déconnecte un signal redirigé vers le contexte principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_signal_disconnect_from_main(gsignal_wrapper_info_t *info) +{ + g_signal_handler_disconnect(info->instance, info->id); + + free(info); } @@ -165,41 +227,44 @@ static void carry_signal_to_main_thread(gsignal_wrapper_info *info, ...) * * ******************************************************************************/ -gulong _g_signal_connect_to_main(gpointer instance, const gchar *signal, GCallback handler, gpointer data, GClosureMarshal marshal, GConnectFlags flags) +gsignal_wrapper_info_t *_g_signal_connect_to_main(gpointer instance, const gchar *signal, GCallback handler, gpointer data, GClosureMarshal marshal, GConnectFlags flags) { + gsignal_wrapper_info_t *result; /* Structure à renvoyer */ guint signal_id; /* Identifiant du signal visé */ GSignalQuery query; /* Information sur le signal */ - gsignal_wrapper_info *info; /* Encapsulation des données */ /* Collection d'informations */ signal_id = g_signal_lookup(signal, G_TYPE_FROM_INSTANCE(instance)); g_signal_query(signal_id, &query); + assert(query.signal_id != 0); + assert(query.return_type == G_TYPE_NONE); /* Allocation adaptée */ - info = calloc(1, sizeof(gsignal_wrapper_info) + sizeof(GValue) * (query.n_params + 1)); + result = malloc(sizeof(gsignal_wrapper_info_t) + sizeof(GType) * query.n_params); - info->instance = instance; + result->instance = instance; if (flags & G_CONNECT_SWAPPED) - info->closure = g_cclosure_new_swap(handler, data, NULL); + result->closure = g_cclosure_new_swap(handler, data, NULL); else - info->closure = g_cclosure_new(handler, data, NULL); + result->closure = g_cclosure_new(handler, data, NULL); - g_closure_ref(info->closure); - g_closure_sink(info->closure); + g_closure_ref(result->closure); + g_closure_sink(result->closure); - g_closure_set_marshal(info->closure, marshal); + g_closure_set_marshal(result->closure, marshal); - info->return_type = query.return_type; - info->n_params = query.n_params; - info->param_types = query.param_types; + result->n_params = query.n_params; + memcpy(result->param_types, query.param_types, sizeof(GType) * query.n_params); - assert(query.return_type == G_TYPE_NONE); + /* Connexion au signal */ + + result->id = g_signal_connect_swapped(instance, signal, G_CALLBACK(carry_signal_to_main_thread), result); - return g_signal_connect_swapped(instance, signal, G_CALLBACK(carry_signal_to_main_thread), info); + return result; } diff --git a/src/glibext/_signal.h b/src/glibext/sigredir.h index 4f0ab4b..f394d77 100644 --- a/src/glibext/_signal.h +++ b/src/glibext/sigredir.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * signal.h - prototypes pour un encadrement des signaux supplémentaire par rapport à celui de la GLib + * sigredir.h - prototypes pour un encadrement des signaux supplémentaire par rapport à celui de la GLib * - * Copyright (C) 2014-2018 Cyrille Bagard + * Copyright (C) 2014-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,20 +21,23 @@ */ -#ifndef _GLIBEXT_SIGNAL_H -#define _GLIBEXT_SIGNAL_H +#ifndef _GLIBEXT_SIGREDIR_H +#define _GLIBEXT_SIGREDIR_H #include <glib-object.h> -#include <gobject/gclosure.h> -#include <glib/gdataset.h> -#include <glib/glist.h> #include <gobject/gsignal.h> +/* Informations concernant une diffusion de signal */ +typedef struct _gsignal_wrapper_info_t gsignal_wrapper_info_t; + /* Reproduit le comportement de la fonction g_signal_connect(). */ -gulong _g_signal_connect_to_main(gpointer, const gchar *, GCallback, gpointer, GClosureMarshal, GConnectFlags); +gsignal_wrapper_info_t *_g_signal_connect_to_main(gpointer, const gchar *, GCallback, gpointer, GClosureMarshal, GConnectFlags); + +/* Déconnecte un signal redirigé vers le contexte principal. */ +void g_signal_disconnect_from_main(gsignal_wrapper_info_t *); #define g_signal_connect_to_main(instance, signal, handler, data, marshal) \ @@ -45,4 +48,4 @@ gulong _g_signal_connect_to_main(gpointer, const gchar *, GCallback, gpointer, G -#endif /* _GLIBEXT_SIGNAL_H */ +#endif /* _GLIBEXT_SIGREDIR_H */ diff --git a/src/glibext/singleton-int.h b/src/glibext/singleton-int.h index 3db17f9..747e64a 100644 --- a/src/glibext/singleton-int.h +++ b/src/glibext/singleton-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton-int.h - définitions internes propres aux réductions du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -29,44 +29,61 @@ +/* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ + + /* Fournit une liste de candidats embarqués par un candidat. */ typedef GSingletonCandidate ** (* list_inner_instances_fc) (const GSingletonCandidate *, size_t *); /* Met à jour une liste de candidats embarqués par un candidat. */ typedef void (* update_inner_instances_fc) (GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -typedef guint (* hash_candidate_fc) (const GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -typedef gboolean (* is_candidate_equal_fc) (const GSingletonCandidate *, const GSingletonCandidate *); - /* Marque un candidat comme figé. */ -typedef void (* set_candidate_ro_fc) (GSingletonCandidate *); +typedef void (* mark_candidate_as_ro_fc) (GSingletonCandidate *); /* Indique si le candidat est figé. */ typedef bool (* is_candidate_ro_fc) (const GSingletonCandidate *); +/* Crée une copie modifiable d'un object unique. */ +typedef GSingletonCandidate * (* dup_candidate_fc) (const GSingletonCandidate *); + /* Instance d'objet visant à être unique (interface) */ -struct _GSingletonCandidateIface +struct _GSingletonCandidateInterface { GTypeInterface base_iface; /* A laisser en premier */ list_inner_instances_fc list_inner; /* Récupération d'internes */ update_inner_instances_fc update_inner; /* Mise à jour des éléments */ - hash_candidate_fc hash; /* Prise d'empreinte */ - is_candidate_equal_fc is_equal; /* Comparaison */ - - set_candidate_ro_fc set_ro; /* Bascule en mode figé */ + mark_candidate_as_ro_fc mark_as_ro; /* Bascule en mode figé */ is_candidate_ro_fc is_ro; /* Consultation de l'état */ + dup_candidate_fc dup; /* Création de copie modifiable*/ + +}; + + + +/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ + + +/* Définition d'un compacteur d'instances de types (instance) */ +struct _GSingletonFactory +{ + GObject parent; /* A laisser en premier */ + + GHashTable *table; /* Suivi des conservations */ + GMutex access; /* Verrou pour la concurrence */ + }; +/* Définition d'un compacteur d'instances de types (classe) */ +struct _GSingletonFactoryClass +{ + GObjectClass parent; /* A laisser en premier */ -/* Redéfinition */ -typedef GSingletonCandidateIface GSingletonCandidateInterface; +}; diff --git a/src/glibext/singleton.c b/src/glibext/singleton.c index 78a3ad4..ed49934 100644 --- a/src/glibext/singleton.c +++ b/src/glibext/singleton.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton.c - réduction du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,52 +25,35 @@ #include <assert.h> +#include <malloc.h> +#include "comparable.h" +#include "hashable.h" #include "singleton-int.h" /* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ + /* Procède à l'initialisation de l'interface de rassemblement. */ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *); +/* Fournit une liste de candidats embarqués par un candidat. */ +static GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); + /* Met à jour une liste de candidats embarqués par un candidat. */ static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint _g_singleton_candidate_hash(GSingletonCandidate *, GList **); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *, GList **); - /* Marque un candidat comme figé. */ -static void _g_singleton_candidate_set_read_only(GSingletonCandidate *, GList **); +static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *); /* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Définition d'un compacteur d'instances de types (instance) */ -struct _GSingletonFactory -{ - GObject parent; /* A laisser en premier */ - - GHashTable *table; /* Suivi des conservations */ - GMutex access; /* Verrou pour la concurrence */ - -}; - -/* Définition d'un compacteur d'instances de types (classe) */ -struct _GSingletonFactoryClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; - - /* Initialise la classe des compacteurs d'instances de types. */ static void g_singleton_factory_class_init(GSingletonFactoryClass *); @@ -90,8 +73,10 @@ static void g_singleton_factory_finalize(GSingletonFactory *); /* ---------------------------------------------------------------------------------- */ -/* Détermine le type d'une interface pour la lecture de binaire. */ -G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT) +/* Détermine le type d'une interface pour la constitution d'objets uniques. */ +G_DEFINE_INTERFACE_WITH_CODE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT,; + g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_COMPARABLE_OBJECT); + g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_HASHABLE_OBJECT)) /****************************************************************************** @@ -108,6 +93,13 @@ G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT) static void g_singleton_candidate_default_init(GSingletonCandidateInterface *iface) { + iface->list_inner = NULL; + iface->update_inner = NULL; + + iface->mark_as_ro = NULL; + iface->is_ro = NULL; + + iface->dup = NULL; } @@ -128,14 +120,17 @@ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *ifa GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *candidate, size_t *count) { GSingletonCandidate **result; /* Instances à retourner */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); if (iface->list_inner == NULL) { + assert(iface->update_inner == NULL); + *count = 0; result = NULL; + } else @@ -162,7 +157,7 @@ GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingleto static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *candidate, GSingletonCandidate **instances, size_t count) { - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); @@ -181,82 +176,36 @@ static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *ca /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* processed = liste de candidats déjà traités. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static guint _g_singleton_candidate_hash(GSingletonCandidate *candidate, GList **processed) +static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *candidate) { - guint result; /* Valeur à retourner */ - GList *skip; /* Détection de boucle */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ GSingletonCandidate **children; /* Instances internes */ size_t count; /* Quantité de ces instances */ size_t i; /* Boucle de parcours */ - skip = g_list_find(*processed, candidate); - - if (skip != NULL) - result = 0; - - else - { - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - - result = iface->hash(candidate); - - *processed = g_list_append(*processed, candidate); - - children = g_singleton_candidate_list_inner_instances(candidate, &count); + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - for (i = 0; i < count; i++) - { - result ^= _g_singleton_candidate_hash(children[i], processed); - g_object_unref(G_OBJECT(children[i])); - } + iface->mark_as_ro(candidate); - if (children != NULL) - free(children); + children = g_singleton_candidate_list_inner_instances(candidate, &count); + for (i = 0; i < count; i++) + { + g_singleton_candidate_mark_as_read_only(children[i]); + unref_object(G_OBJECT(children[i])); } - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * -* * -* Retour : Empreinte de l'élément représenté. * -* * -* Remarques : - * -* * -******************************************************************************/ - -guint g_singleton_candidate_hash(GSingletonCandidate *candidate) -{ - guint result; /* Valeur à retourner */ - GList *processed; /* Suivi des traitements */ - - processed = NULL; - - result = _g_singleton_candidate_hash(candidate, &processed); - - assert(processed != NULL); - - g_list_free(processed); - - return result; + if (children != NULL) + free(children); } @@ -264,116 +213,43 @@ guint g_singleton_candidate_hash(GSingletonCandidate *candidate) /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * -* processed = liste de candidats déjà traités. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other, GList **processed) +bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate) { - gboolean result; /* Bilan à renvoyer */ - GList *skip; /* Détection de boucle */ - GSingletonCandidateIface *iface; /* Interface utilisée */ - GSingletonCandidate **children[2]; /* Instances internes */ - size_t count[2]; /* Quantité de ces instances */ + bool result; /* Etat à retourner */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ +#ifndef NDEBUG + GSingletonCandidate **children; /* Instances internes */ + size_t count; /* Quantité de ces instances */ size_t i; /* Boucle de parcours */ +#endif - skip = g_list_find(processed[0], candidate); - - if (skip != NULL) - result = (g_list_find(processed[1], other) != NULL); - - else - { - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - - result = iface->is_equal(candidate, other); - - processed[0] = g_list_append(processed[0], candidate); - processed[1] = g_list_append(processed[1], other); - - if (!result) - goto done; - - children[0] = g_singleton_candidate_list_inner_instances(candidate, &count[0]); - children[1] = g_singleton_candidate_list_inner_instances(other, &count[1]); - - if (count[0] != count[1]) - { - for (i = 0; i < count[0]; i++) - g_object_unref(G_OBJECT(children[0][i])); - - for (i = 0; i < count[1]; i++) - g_object_unref(G_OBJECT(children[1][i])); - - } - - else - { - for (i = 0; i < count[0] && result; i++) - { - result = _g_singleton_candidate_is_equal(children[0][i], children[1][i], processed); - g_object_unref(G_OBJECT(children[0][i])); - g_object_unref(G_OBJECT(children[1][i])); - } - - for (; i < count[0]; i++) - { - g_object_unref(G_OBJECT(children[0][i])); - g_object_unref(G_OBJECT(children[1][i])); - } + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - if (children[0] != NULL) - free(children[0]); + result = iface->is_ro(candidate); - if (children[1] != NULL) - free(children[1]); +#ifndef NDEBUG - } + children = g_singleton_candidate_list_inner_instances(candidate, &count); + for (i = 0; i < count; i++) + { + assert(result == g_singleton_candidate_is_read_only(children[i])); + unref_object(G_OBJECT(children[i])); } - done: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * -* * -* Description : Détermine si deux candidats à l'unicité sont identiques. * -* * -* Retour : Bilan de la comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other) -{ - gboolean result; /* Bilan à renvoyer */ - GList *processed[2]; /* Suivi des traitements */ + if (children != NULL) + free(children); - processed[0] = NULL; - processed[1] = NULL; - - result = _g_singleton_candidate_is_equal(candidate, other, processed); - - assert(processed[0] != NULL); - assert(processed[1] != NULL); - - g_list_free(processed[0]); - g_list_free(processed[1]); +#endif return result; @@ -383,97 +259,60 @@ gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSinglet /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* processed = liste de candidats déjà traités. * * * -* Description : Marque un candidat comme figé. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : - * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static void _g_singleton_candidate_set_read_only(GSingletonCandidate *candidate, GList **processed) +GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *candidate) { - GList *skip; /* Détection de boucle */ - GSingletonCandidateIface *iface; /* Interface utilisée */ - GSingletonCandidate **children; /* Instances internes */ - size_t count; /* Quantité de ces instances */ + GSingletonCandidate *result; /* Instance à retourner */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ + size_t count; /* Quantité d'objets internes */ + GSingletonCandidate **children; /* Liste d'instances internes */ size_t i; /* Boucle de parcours */ + GSingletonCandidate **new_children; /* Nouvelle liste d'instances */ - skip = g_list_find(*processed, candidate); + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - if (skip == NULL) - { - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + result = iface->dup(candidate); - iface->set_ro(candidate); + assert(!g_singleton_candidate_is_read_only(result)); - *processed = g_list_append(*processed, candidate); + children = g_singleton_candidate_list_inner_instances(candidate, &count); - children = g_singleton_candidate_list_inner_instances(candidate, &count); + if (count > 0) + { + new_children = malloc(count * sizeof(GSingletonCandidate *)); for (i = 0; i < count; i++) { - _g_singleton_candidate_set_read_only(candidate, processed); - g_object_unref(G_OBJECT(children[i])); - } - - if (children != NULL) - free(children); - - } - -} + new_children[i] = g_singleton_candidate_dup(children[i]); + assert(!g_singleton_candidate_is_read_only(new_children[i])); -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Marque un candidat comme figé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_singleton_candidate_set_read_only(GSingletonCandidate *candidate) -{ - GList *processed; /* Suivi des traitements */ - - processed = NULL; - - _g_singleton_candidate_set_read_only(candidate, &processed); - - assert(processed != NULL); + } - g_list_free(processed); - -} + g_singleton_candidate_update_inner_instances(result, new_children, count); + for (i = 0; i < count; i++) + { + unref_object(G_OBJECT(new_children[i])); + unref_object(G_OBJECT(children[i])); + } -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Indique si le candidat est figé. * -* * -* Retour : true si le contenu du candidat ne peut plus être modifié. * -* * -* Remarques : - * -* * -******************************************************************************/ + free(new_children); -bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate) -{ - bool result; /* Etat à retourner */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + } - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + if (children != NULL) + free(children); - result = iface->is_ro(candidate); + assert(G_OBJECT_TYPE(result) == G_OBJECT_TYPE(candidate)); return result; @@ -528,8 +367,8 @@ static void g_singleton_factory_class_init(GSingletonFactoryClass *klass) static void g_singleton_factory_init(GSingletonFactory *factory) { - factory->table = g_hash_table_new_full((GHashFunc)g_singleton_candidate_hash, - (GEqualFunc)g_singleton_candidate_is_equal, + factory->table = g_hash_table_new_full((GHashFunc)g_hashable_object_hash, + (GEqualFunc)g_comparable_object_is_equal, g_object_unref, NULL); g_mutex_init(&factory->access); @@ -688,7 +527,7 @@ GSingletonCandidate *g_singleton_factory_get_instance(GSingletonFactory *factory g_hash_table_add(factory->table, candidate); #endif - g_singleton_candidate_set_read_only(candidate); + g_singleton_candidate_mark_as_read_only(candidate); result = candidate; diff --git a/src/glibext/singleton.h b/src/glibext/singleton.h index 629687a..11afffd 100644 --- a/src/glibext/singleton.h +++ b/src/glibext/singleton.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton.h - prototypes pour la réduction du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,71 +25,36 @@ #define _GLIBEXT_SINGLETON_H -#include <glib-object.h> #include <stdbool.h> - -/* Définition d'un compacteur d'instances de types (instance) */ -typedef struct _GSingletonFactory GSingletonFactory; +#include "helpers.h" /* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ -#define G_TYPE_SINGLETON_CANDIDATE (g_singleton_candidate_get_type()) -#define G_SINGLETON_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidate)) -#define G_SINGLETON_CANDIDATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidateIface)) -#define G_IS_SINGLETON_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SINGLETON_CANDIDATE)) -#define G_IS_SINGLETON_CANDIDATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_SINGLETON_CANDIDATE)) -#define G_SINGLETON_CANDIDATE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidateIface)) - - -/* Instance d'objet visant à être unique (coquille vide) */ -typedef struct _GSingletonCandidate GSingletonCandidate; - -/* Instance d'objet visant à être unique (interface) */ -typedef struct _GSingletonCandidateIface GSingletonCandidateIface; - - -/* Détermine le type d'une interface pour la lecture de binaire. */ -GType g_singleton_candidate_get_type(void) G_GNUC_CONST; +#define G_TYPE_SINGLETON_CANDIDATE (g_singleton_candidate_get_type()) -/* Fournit une liste de candidats embarqués par un candidat. */ -GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); +DECLARE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G, SINGLETON_CANDIDATE); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -guint g_singleton_candidate_hash(GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -gboolean g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *); - -/* Marque un candidat comme figé. */ -void g_singleton_candidate_set_read_only(GSingletonCandidate *); /* Indique si le candidat est figé. */ bool g_singleton_candidate_is_read_only(const GSingletonCandidate *); +/* Crée une copie modifiable d'un object unique. */ +GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *); -/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ - -#define G_TYPE_SINGLETON_FACTORY g_singleton_factory_get_type() -#define G_SINGLETON_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactory)) -#define G_IS_SINGLETON_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SINGLETON_FACTORY)) -#define G_SINGLETON_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass)) -#define G_IS_SINGLETON_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SINGLETON_FACTORY)) -#define G_SINGLETON_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass)) +/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Définition d'un compacteur d'instances de types (classe) */ -typedef struct _GSingletonFactoryClass GSingletonFactoryClass; +#define G_TYPE_SINGLETON_FACTORY (g_singleton_factory_get_type()) +DECLARE_GTYPE(GSingletonFactory, g_singleton_factory, G, SINGLETON_FACTORY); -/* Indique le type défini pour une mémoire de types d'objets. */ -GType g_singleton_factory_get_type(void); /* Crée un compacteur d'instances de types. */ GSingletonFactory *g_singleton_factory_new(void); diff --git a/src/analysis/storage/storage-int.h b/src/glibext/storage-int.h index 4883aa8..d89e1c8 100644 --- a/src/analysis/storage/storage-int.h +++ b/src/glibext/storage-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * storage.h - prototypes internes pour la conservation sur disque d'objets construits + * storage-int.h - prototypes internes pour la conservation sur disque d'objets construits * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,11 +21,13 @@ */ -#ifndef _ANALYSIS_STORAGE_STORAGE_INT_H -#define _ANALYSIS_STORAGE_STORAGE_INT_H +#ifndef _GLIBEXT_STORAGE_INT_H +#define _GLIBEXT_STORAGE_INT_H #include "storage.h" +#include "tpmem.h" +#include "../common/szbin.h" @@ -44,9 +46,12 @@ struct _GObjectStorage { GObject parent; /* A laisser en premier */ - GTypeMemory *tpmem; /* Mémorisation de types */ + sized_binary_t type; /* Type de conservation */ + uint8_t version; /* Version correspondante */ + + sized_binary_t uid; /* Identifiant de distinction */ - char *hash; /* Empreinte du contenu */ + GTypeMemory *tpmem; /* Mémorisation de types */ storage_backend_t *backends; /* Gestionnaires existants */ size_t count; /* Quantité de gestionnaires */ @@ -62,5 +67,9 @@ struct _GObjectStorageClass }; +/* Met en place un support d'une conservation d'objets en place. */ +bool g_object_storage_create(GObjectStorage *, const char *, uint8_t, const char *); + + -#endif /* _ANALYSIS_STORAGE_STORAGE_INT_H */ +#endif /* _GLIBEXT_STORAGE_INT_H */ diff --git a/src/analysis/storage/storage.c b/src/glibext/storage.c index 610a0f6..0a3c4e7 100644 --- a/src/analysis/storage/storage.c +++ b/src/glibext/storage.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * storage.c - conservation hors mémoire d'objets choisis * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,20 +28,23 @@ #include <malloc.h> #include <string.h> #include <unistd.h> -#include <stdarg.h> +#include <zip.h> #include "storage-int.h" -#include "../db/misc/rlestr.h" -#include "../../common/io.h" -#include "../../common/leb128.h" -#include "../../common/pathname.h" -#include "../../core/logs.h" +#include "../common/cpp.h" +#include "../common/pathname.h" +#include "../core/logs.h" - -#define STORAGE_MAGIC "CSTR" -#define STORAGE_NUMBER "\x00\x01" +/** + * Historique du format : + * + * - 09/03/25 : 1.0 (version initiale) + * + */ +#define STORAGE_MAGIC "COBSTR" +#define STORAGE_NUMBER "\x01\x00" /* Initialise la classe des conservations d'objets en place. */ @@ -51,10 +54,13 @@ static void g_object_storage_class_init(GObjectStorageClass *); static void g_object_storage_init(GObjectStorage *); /* Supprime toutes les références externes. */ -static void g_object_storage_dispose(GObjectStorage *); +static void g_object_storage_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_object_storage_finalize(GObjectStorage *); +static void g_object_storage_finalize(GObject *); + +/* Assure l'inexistence d'un groupe avec un nom donné. */ +static bool g_object_storage_has_no_backend_named(GObjectStorage *, const char *); /* Retrouve l'encadrement pour un nouveau groupe d'objets. */ static storage_backend_t *g_object_storage_find_backend(GObjectStorage *, const char *); @@ -62,11 +68,8 @@ static storage_backend_t *g_object_storage_find_backend(GObjectStorage *, const /* Ajoute le support d'un nouveau groupe d'objets construits. */ static bool g_object_storage_add_backend(GObjectStorage *, const char *, storage_backend_t **); -/* Extrait d'un tampon des enregistrements spécifiques. */ -static bool g_object_storage_load_backend(GObjectStorage *, packed_buffer_t *); - -/* Place dans un tampon les données liées à des enregistrements. */ -static bool pack_storage_backend(const storage_backend_t *, packed_buffer_t *); +/* Charge un objet à partir de données rassemblées. */ +static GSerializableObject *g_object_storage_load_object_unlocked(GObjectStorage *, const char *, off64_t); @@ -92,8 +95,8 @@ static void g_object_storage_class_init(GObjectStorageClass *klass) object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_object_storage_dispose; - object->finalize = (GObjectFinalizeFunc)g_object_storage_finalize; + object->dispose = g_object_storage_dispose; + object->finalize = g_object_storage_finalize; } @@ -112,9 +115,12 @@ static void g_object_storage_class_init(GObjectStorageClass *klass) static void g_object_storage_init(GObjectStorage *storage) { - storage->tpmem = g_type_memory_new(); + init_sized_binary(&storage->type); + storage->version = 0; - storage->hash = NULL; + init_sized_binary(&storage->uid); + + storage->tpmem = g_type_memory_new(); storage->backends = NULL; storage->count = 0; @@ -125,7 +131,7 @@ static void g_object_storage_init(GObjectStorage *storage) /****************************************************************************** * * -* Paramètres : storage = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -135,18 +141,22 @@ static void g_object_storage_init(GObjectStorage *storage) * * ******************************************************************************/ -static void g_object_storage_dispose(GObjectStorage *storage) +static void g_object_storage_dispose(GObject *object) { + GObjectStorage *storage; /* Version spécialisée */ + + storage = G_OBJECT_STORAGE(object); + g_clear_object(&storage->tpmem); - G_OBJECT_CLASS(g_object_storage_parent_class)->dispose(G_OBJECT(storage)); + G_OBJECT_CLASS(g_object_storage_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : storage = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -156,18 +166,27 @@ static void g_object_storage_dispose(GObjectStorage *storage) * * ******************************************************************************/ -static void g_object_storage_finalize(GObjectStorage *storage) +static void g_object_storage_finalize(GObject *object) { + GObjectStorage *storage; /* Version spécialisée */ size_t i; /* Boucle de parcours */ storage_backend_t *backend; /* Gestionnaire à manipuler */ int ret; /* Bilan d'un appel */ + storage = G_OBJECT_STORAGE(object); + g_mutex_lock(&storage->mutex); for (i = 0; i < storage->count; i++) { backend = &storage->backends[i]; + /** + * Chargement incomplet depuis g_object_storage_load(). + */ + if (backend->name == NULL) + break; + if (backend->fd != -1) close(backend->fd); else @@ -193,17 +212,20 @@ static void g_object_storage_finalize(GObjectStorage *storage) g_mutex_clear(&storage->mutex); - if (storage->hash != NULL) - free(storage->hash); + exit_sized_binary(&storage->type); + + exit_sized_binary(&storage->uid); - G_OBJECT_CLASS(g_object_storage_parent_class)->finalize(G_OBJECT(storage)); + G_OBJECT_CLASS(g_object_storage_parent_class)->finalize(object); } /****************************************************************************** * * -* Paramètres : loaded = contenu binaire à associer. * +* Paramètres : type = type global à indiquer dans une conservation. * +* version = numéro de version associé. * +* uid = identifiant arbitraire mais unique pour distinguer.* * * * Description : Crée le support d'une conservation d'objets en place. * * * @@ -213,13 +235,14 @@ static void g_object_storage_finalize(GObjectStorage *storage) * * ******************************************************************************/ -GObjectStorage *g_object_storage_new(const char *hash) +GObjectStorage *g_object_storage_new(const char *type, uint8_t version, const char *uid) { GObjectStorage *result; /* Structure à retourner */ result = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); - result->hash = strdup(hash); + if (!g_object_storage_create(result, type, version, uid)) + g_clear_object(&result); return result; @@ -228,7 +251,39 @@ GObjectStorage *g_object_storage_new(const char *hash) /****************************************************************************** * * -* Paramètres : pbuf = zone tampon à lire. * +* Paramètres : storage = stockage d'objets à initialiser. * +* type = type global à indiquer dans une conservation. * +* version = numéro de version associé. * +* uid = identifiant arbitraire mais unique pour distinguer.* +* * +* Description : Met en place un support d'une conservation d'objets en place.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_object_storage_create(GObjectStorage *storage, const char *type, uint8_t version, const char *uid) +{ + bool result; /* Bilan à retourner */ + + result = true; + + dup_into_sized_binary(&storage->type, type, strlen(type)); + + storage->version = version; + + dup_into_sized_binary(&storage->uid, uid, strlen(uid) + 1); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : filename = fichier de source à traiter. * * * * Description : Charge le support d'une conservation d'objets en place. * * * @@ -238,140 +293,297 @@ GObjectStorage *g_object_storage_new(const char *hash) * * ******************************************************************************/ -GObjectStorage *g_object_storage_load(packed_buffer_t *pbuf) +GObjectStorage *g_object_storage_load(const char *filename) { GObjectStorage *result; /* Structure à retourner */ - char header[6]; /* Entête attendue des données */ + GObjectStorage *storage; /* Structure en construction */ + int err; /* Eventuel code d'erreur */ + zip_t *archive; /* Archive ZIP à manipuler */ + zip_error_t error; /* Suivi des erreurs obtenues */ + char *tpmem_filename; /* Chemin d'accès pour types */ + zip_int64_t entries_count; /* Nombre d'éléments ZIP */ + void *data; /* Données (décompressées) */ + zip_stat_t stats; /* Information sur les données */ + zip_file_t *file; /* Echantillon à extraire */ + zip_int64_t got; /* Nombre d'octets lus */ + int ret; /* Bilan d'un appel */ + const void *pos; /* Tête de lecture */ + const void *max; /* Fin des données lisibles */ bool status; /* Bilan d'une extraction */ - rle_string str; /* Chaîne à conserver */ - uleb128_t count; /* Nombre de groupes à charger */ - uleb128_t i; /* Boucle de parcours */ + char *prefix; /* Début de nom de fichier */ + int fd; /* Descripteur de flux ouvert */ + off_t moved; /* Nouvelle position établie */ + zip_int64_t i; /* Boucle de parcours */ + storage_backend_t *backend; /* Informations à intégrer */ + const char *slash; /* Pointeur vers un caractère /*/ result = NULL; - status = extract_packed_buffer(pbuf, header, 6, false); - if (!status) goto quick_exit; + storage = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); - if (strncmp(header, STORAGE_MAGIC STORAGE_NUMBER, 6) != 0) - goto quick_exit; + archive = zip_open(filename, ZIP_RDONLY, &err); + if (archive == NULL) + { + zip_error_init_with_code(&error, err); + LOG_ERROR_ZIP("zip_open", &error); + goto exit; + } + + zip_error_init(&error); + + tpmem_filename = NULL; + + /* Validation du nombre d'entrées */ + + entries_count = zip_get_num_entries(archive, ZIP_FL_UNCHANGED); - setup_empty_rle_string(&str); + if (entries_count < 2) + goto exit_with_archive; - status = unpack_rle_string(&str, pbuf); - if (!status) goto quick_exit; + data = NULL; - if (get_rle_string(&str) == NULL) + /* Extraction de la partie de contrôle */ + + ret = zip_stat_index(archive, 0, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) { - exit_rle_string(&str); - goto quick_exit; + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; } - result = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); - - result->hash = strdup(get_rle_string(&str)); + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; - exit_rle_string(&str); + if (strcmp(stats.name, "control") != 0) + goto exit_with_archive; - status = g_type_memory_load_types(result->tpmem, pbuf); - if (!status) goto exit_while_loading; + if (stats.size < (6 + 2 + 1 + 1 + 1)) + goto exit_with_archive; - status = unpack_uleb128(&count, pbuf); + file = zip_fopen_index(archive, 0, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; + } - for (i = 0; i < count && status; i++) - status = g_object_storage_load_backend(result, pbuf); + data = malloc(stats.size); - exit_while_loading: + got = zip_fread(file, data, stats.size); - if (!status) + ret = zip_fclose(file); + if (ret != 0) { - g_object_unref(G_OBJECT(result)); - result = NULL; + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; } - quick_exit: + if (got != stats.size) + goto exit_with_data; - return result; + if (memcmp(data, STORAGE_MAGIC, 6) != 0) + goto exit_with_data; -} + if (memcmp(((uint8_t *)data) + 6, STORAGE_NUMBER, 2) != 0) + goto exit_with_data; + pos = (uint8_t *)data + 8; + max = (uint8_t *)data + got; -/****************************************************************************** -* * -* Paramètres : storage = gestionnaire de conservations à manipuler. * -* pbuf = zone tampon à remplir. [OUT] * -* * -* Description : Sauvegarde le support d'une conservation d'objets en place. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ + status = unpack_sized_binary(&storage->type, &pos, max); + if (!status) goto exit_with_data; -bool g_object_storage_store(GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - rle_string str; /* Chaîne à conserver */ - size_t i; /* Boucle de parcours */ + if (pos >= max) + goto exit_with_data; - result = extend_packed_buffer(pbuf, STORAGE_MAGIC STORAGE_NUMBER, 6, false); + storage->version = *(uint8_t *)pos; + pos = (uint8_t *)pos + 1; - if (result) + unpack_sized_binary_as_string(&storage->uid, &pos, max); + if (!status) goto exit_with_data; + + if (pos != max) + goto exit_with_data; + + free(data); + data = NULL; + + /* Extraction de la conservation des types */ + + ret = zip_stat_index(archive, 1, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) { - init_static_rle_string(&str, storage->hash); + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } - result = pack_rle_string(&str, pbuf); + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; - exit_rle_string(&str); + if (strcmp(stats.name, "types") != 0) + goto exit_with_archive; + file = zip_fopen_index(archive, 1, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; } - g_mutex_lock(&storage->mutex); + data = malloc(stats.size); - if (result) - result = g_type_memory_store_types(storage->tpmem, pbuf); + got = zip_fread(file, data, stats.size); - if (result) - result = pack_uleb128((uleb128_t []){ storage->count }, pbuf); + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } - for (i = 0; i < storage->count && result; i++) - result = pack_storage_backend(&storage->backends[i], pbuf); + asprintf(&prefix, "%s-types", storage->uid.static_data); - g_mutex_unlock(&storage->mutex); + fd = make_tmp_file(prefix, "cache", &tpmem_filename); - return result; + free(prefix); -} + if (fd == -1) + goto exit_with_data; + status = safe_write(fd, data, stats.size); + if (!status) + { + close(fd); + goto exit_with_data; + } -/****************************************************************************** -* * -* Paramètres : storage = gestionnaire de conservations à compléter. * -* name = désignation d'un nouveau groupe d'objets. * -* * -* Description : Retrouve l'encadrement pour un nouveau groupe d'objets. * -* * -* Retour : Informations liées à un groupe ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ + moved = lseek(fd, 0, SEEK_SET); + if (moved == ((off_t)-1)) + { + LOG_ERROR_N("lseek"); + close(fd); + goto exit_with_data; + } -static storage_backend_t *g_object_storage_find_backend(GObjectStorage *storage, const char *name) -{ - storage_backend_t *result; /* Encadrement à retourner */ - size_t i; /* Boucle de parcours */ + status = g_type_memory_load(storage->tpmem, fd); - assert(!g_mutex_trylock(&storage->mutex)); + close(fd); - for (i = 0; i < storage->count; i++) - if (strcmp(storage->backends[i].name, name) == 0) - break; + if (!status) + goto exit_with_data; - if (i == storage->count) - result = NULL; - else - result = &storage->backends[i]; + free(data); + data = NULL; + + /* Extraction des différents objects restants */ + + if (entries_count > 2) + { + storage->count = entries_count - 2; + storage->backends = calloc(storage->count, sizeof(storage_backend_t)); + + for (i = 2; i < entries_count; i++) + { + backend = &storage->backends[i - 2]; + + ret = zip_stat_index(archive, i, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) + { + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } + + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; + + if (strncmp(stats.name, SL("backends/")) != 0) + goto exit_with_archive; + + slash = strchr(stats.name, '/'); + + if (slash == NULL) + goto exit_with_archive; + + if (strchr(slash + 1, '/') != NULL) + goto exit_with_archive; + + if (!g_object_storage_has_no_backend_named(storage, slash + 1)) + goto exit_with_archive; + + file = zip_fopen_index(archive, i, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; + } + + data = malloc(stats.size); + + got = zip_fread(file, data, stats.size); + + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } + + backend->name = strdup(slash + 1); + + asprintf(&prefix, "%s-%s", storage->uid.static_data, backend->name); + + backend->fd = make_tmp_file(prefix, "cache", &backend->filename); + + free(prefix); + + if (backend->fd == -1) + goto exit_with_data; + + status = safe_write(backend->fd, data, stats.size); + if (!status) goto exit_with_data; + + moved = lseek(backend->fd, 0, SEEK_SET); + if (moved == ((off_t)-1)) + { + LOG_ERROR_N("lseek"); + goto exit_with_data; + } + + free(data); + data = NULL; + + } + + } + + /* Clôture des opérations */ + + result = storage; + ref_object(storage); + + exit_with_data: + + if (data != NULL) + free(data); + + exit_with_archive: + + ret = zip_close(archive); + + if (ret != 0) + LOG_ERROR_ZIP("zip_close", zip_get_error(archive)); + + if (tpmem_filename != NULL) + unlink(tpmem_filename); + + zip_error_fini(&error); + + exit: + + unref_object(storage); return result; @@ -380,11 +592,10 @@ static storage_backend_t *g_object_storage_find_backend(GObjectStorage *storage, /****************************************************************************** * * -* Paramètres : storage = gestionnaire de conservations à compléter. * -* name = désignation d'un nouveau groupe d'objets. * -* backend = support mis en place pour les enregistrements. * +* Paramètres : storage = gestionnaire de conservations à manipuler. * +* filename = fichier de destination à constituer. * * * -* Description : Ajoute le support d'un nouveau groupe d'objets construits. * +* Description : Sauvegarde le support d'une conservation d'objets en place. * * * * Retour : Bilan de l'opération. * * * @@ -392,46 +603,199 @@ static storage_backend_t *g_object_storage_find_backend(GObjectStorage *storage, * * ******************************************************************************/ -static bool g_object_storage_add_backend(GObjectStorage *storage, const char *name, storage_backend_t **backend) +bool g_object_storage_store(GObjectStorage *storage, const char *filename) { bool result; /* Bilan à retourner */ + int err; /* Eventuel code d'erreur */ + zip_t *archive; /* Archive ZIP à manipuler */ + zip_error_t error; /* Suivi des erreurs obtenues */ + char *tpmem_filename; /* Chemin d'accès pour types */ + void *type_buf; /* Données pour le type */ + size_t type_buflen; /* Quantité de ces données */ + void *uid_buf; /* Données pour l'identifiant */ + size_t uid_buflen; /* Quantité de ces données */ + size_t control_len; /* Taille des premières données*/ + uint8_t *control; /* Premières données du fichier*/ + zip_source_t *zip_data; /* Données ZIP à intégrer */ + zip_int64_t index; /* Nouvel index du contenu */ + int ret; /* Bilan d'un appel */ char *prefix; /* Début de nom de fichier */ - char *filename; /* Chemin d'accès aux données */ int fd; /* Descripteur de flux ouvert */ + bool status; /* Bilan d'une écriture */ + size_t i; /* Boucle de parcours */ + char *zip_name; /* Destination pour l'archive */ result = false; - *backend = NULL; + archive = zip_open(filename, ZIP_CREATE | ZIP_TRUNCATE, &err); + if (archive == NULL) + { + zip_error_init_with_code(&error, err); + LOG_ERROR_ZIP("zip_open", &error); + goto exit; + } - assert(!g_mutex_trylock(&storage->mutex)); + zip_error_init(&error); - if (g_object_storage_find_backend(storage, name) != NULL) - goto exit; + tpmem_filename = NULL; - /* Préparatifs */ + /* Fichier de contrôle */ - asprintf(&prefix, "%s-%s", storage->hash, name); + type_buf = pack_sized_binary(&storage->type, &type_buflen); - fd = make_tmp_file(prefix, "cache", &filename); + uid_buf = pack_sized_binary_as_string(&storage->uid, &uid_buflen); + + assert((sizeof(STORAGE_MAGIC) - 1 + sizeof(STORAGE_NUMBER) - 1) == 8); + + control_len = 8 + type_buflen + 1 + uid_buflen; + control = malloc(control_len * sizeof(uint8_t)); + + memcpy(control, STORAGE_MAGIC, 6); + memcpy(control + 6, STORAGE_NUMBER, 2); + + memcpy(control + 8, type_buf, type_buflen); + + control[8 + type_buflen] = storage->version; + + memcpy(control + 8 + type_buflen + 1, uid_buf, uid_buflen); + + zip_data = zip_source_buffer_create(control, control_len, 0, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_buffer_create", &error); + goto exit_with_control; + } + + index = zip_file_add(archive, "control", zip_data, ZIP_FL_ENC_UTF_8); + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_control; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_STORE, 0 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_control; + } + + /* Composants de la conservation */ + + g_mutex_lock(&storage->mutex); + + /* Conservation des types */ + + asprintf(&prefix, "%s-types", storage->uid.static_data); + + fd = make_tmp_file(prefix, "cache", &tpmem_filename); free(prefix); if (fd == -1) - goto exit; + goto exit_with_lock; - /* Inscription en bonne et due forme */ + status = g_type_memory_store(storage->tpmem, fd); - storage->backends = realloc(storage->backends, ++storage->count * sizeof(storage_backend_t)); + close(fd); - *backend = &storage->backends[storage->count - 1]; + if (!status) + goto exit_with_lock; - (*backend)->name = strdup(name); + zip_data = zip_source_file_create(tpmem_filename, 0, -1 /* ZIP_LENGTH_TO_END */, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_file_create", &error); + goto exit_with_lock; + } - (*backend)->filename = filename; - (*backend)->fd = fd; + index = zip_file_add(archive, "types", zip_data, ZIP_FL_ENC_UTF_8); + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_lock; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_DEFLATE, 9 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_lock; + } + + /* Conservation des objets */ + + for (i = 0; i < storage->count; i++) + { + zip_data = zip_source_file_create(storage->backends[i].filename, 0, -1 /* ZIP_LENGTH_TO_END */, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_file_create", &error); + goto exit_with_lock; + } + + /** + * Pas besoin de distinguer les chemins UNIX et Windows ici. + * + * Cf. https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT : + * + * 4.4.17 file name: (Variable) + * + * The path stored MUST NOT contain a drive or + * device letter, or a leading slash. All slashes + * MUST be forward slashes '/' as opposed to + * backwards slashes '\' for compatibility with Amiga + * and UNIX file systems etc. If input came from standard + * input, there is no file name field. + * + */ + + asprintf(&zip_name, "backends/%s", storage->backends[i].name); + + index = zip_file_add(archive, zip_name, zip_data, ZIP_FL_ENC_UTF_8); + + free(zip_name); + + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_lock; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_DEFLATE, 9 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_lock; + } + + } result = true; + /* Clôture des opérations */ + + exit_with_lock: + + g_mutex_unlock(&storage->mutex); + + exit_with_control: + + ret = zip_close(archive); + + if (ret != 0) + LOG_ERROR_ZIP("zip_close", zip_get_error(archive)); + + free(control); + + if (tpmem_filename != NULL) + unlink(tpmem_filename); + + zip_error_fini(&error); + exit: return result; @@ -441,71 +805,67 @@ static bool g_object_storage_add_backend(GObjectStorage *storage, const char *na /****************************************************************************** * * -* Paramètres : storage = gestionnaire de conservations à compléter. * -* pbuf = zone tampon à lire. * +* Paramètres : storage = gestionnaire de conservations à consulter. * +* name = désignation d'un nouveau groupe d'objets. * * * -* Description : Extrait d'un tampon des enregistrements spécifiques. * +* Description : Assure l'inexistence d'un groupe avec un nom donné. * * * -* Retour : Bilan de l'opération. * +* Retour : Bilan des recherches. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_object_storage_load_backend(GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_object_storage_has_no_backend_named(GObjectStorage *storage, const char *name) { bool result; /* Bilan à retourner */ - rle_string str; /* Chaîne à conserver */ - bool status; /* Bilan de lecture de contenu */ - storage_backend_t *backend; /* Informations à intégrer */ - uleb128_t length; /* Taille des données à charger*/ - off_t moved; /* Nouvelle position établie */ - - result = false; - - g_mutex_lock(&storage->mutex); - - /* Récupération du nom et création du support */ - - setup_empty_rle_string(&str); + size_t i; /* Boucle de parcours */ - status = unpack_rle_string(&str, pbuf); - if (!status) goto exit; + result = true; - if (get_rle_string(&str) == NULL) + for (i = 0; i < storage->count && result; i++) { - exit_rle_string(&str); - goto exit; - } - - status = g_object_storage_add_backend(storage, get_rle_string(&str), &backend); + if (storage->backends[i].name == NULL) + break; - exit_rle_string(&str); + if (strcmp(storage->backends[i].name, name) == 0) + result = false; - if (!status) goto exit; + } - /* Récupération du contenu */ + return result; - status = unpack_uleb128(&length, pbuf); - if (!status) goto exit; +} - status = safe_write(backend->fd, pbuf->data + pbuf->pos, length); - if (!status) goto exit; - advance_packed_buffer(pbuf, length); +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à compléter. * +* name = désignation d'un nouveau groupe d'objets. * +* * +* Description : Retrouve l'encadrement pour un nouveau groupe d'objets. * +* * +* Retour : Informations liées à un groupe ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ - moved = lseek(backend->fd, 0, SEEK_SET); - if (moved == ((off_t)-1)) - { - LOG_ERROR_N("lseek"); - goto exit; - } +static storage_backend_t *g_object_storage_find_backend(GObjectStorage *storage, const char *name) +{ + storage_backend_t *result; /* Encadrement à retourner */ + size_t i; /* Boucle de parcours */ - result = true; + assert(!g_mutex_trylock(&storage->mutex)); - exit: + for (i = 0; i < storage->count; i++) + if (strcmp(storage->backends[i].name, name) == 0) + break; - g_mutex_unlock(&storage->mutex); + if (i == storage->count) + result = NULL; + else + result = &storage->backends[i]; return result; @@ -514,10 +874,11 @@ static bool g_object_storage_load_backend(GObjectStorage *storage, packed_buffer /****************************************************************************** * * -* Paramètres : backend = stockage des enregistrements spécifiques. * -* pbuf = zone tampon à remplir. [OUT] * +* Paramètres : storage = gestionnaire de conservations à compléter. * +* name = désignation d'un nouveau groupe d'objets. * +* backend = support mis en place pour les enregistrements. * * * -* Description : Place dans un tampon les données liées à des enregistrements.* +* Description : Ajoute le support d'un nouveau groupe d'objets construits. * * * * Retour : Bilan de l'opération. * * * @@ -525,72 +886,45 @@ static bool g_object_storage_load_backend(GObjectStorage *storage, packed_buffer * * ******************************************************************************/ -static bool pack_storage_backend(const storage_backend_t *backend, packed_buffer_t *pbuf) +static bool g_object_storage_add_backend(GObjectStorage *storage, const char *name, storage_backend_t **backend) { bool result; /* Bilan à retourner */ - rle_string str; /* Chaîne à conserver */ - bool status; /* Bilan de lecture de contenu */ - off_t current; /* Position courante */ - off_t moved; /* Nouvelle position établie */ - void *data; /* Données à transférer */ + char *prefix; /* Début de nom de fichier */ + char *filename; /* Chemin d'accès aux données */ + int fd; /* Descripteur de flux ouvert */ result = false; - /* Inscription du nom */ + *backend = NULL; - init_static_rle_string(&str, backend->name); + assert(!g_mutex_trylock(&storage->mutex)); - status = pack_rle_string(&str, pbuf); + if (g_object_storage_find_backend(storage, name) != NULL) + goto exit; - exit_rle_string(&str); + /* Préparatifs */ - if (!status) goto exit; + asprintf(&prefix, "%s-%s", storage->uid.static_data, name); - /* Inscription du contenu */ + fd = make_tmp_file(prefix, "cache", &filename); - current = lseek(backend->fd, 0, SEEK_CUR); - if (current == ((off_t)-1)) - { - LOG_ERROR_N("lseek"); - goto exit; - } + free(prefix); - moved = lseek(backend->fd, 0, SEEK_SET); - if (moved == ((off_t)-1)) - { - LOG_ERROR_N("lseek"); + if (fd == -1) goto exit; - } - data = malloc(current); - if (data == NULL) - { - LOG_ERROR_N("malloc"); - goto restore; - } - - status = safe_read(backend->fd, data, current); - if (!status) goto free_mem; - - status = pack_uleb128((uleb128_t []){ current }, pbuf); - if (!status) goto free_mem; - - status = extend_packed_buffer(pbuf, data, current, false); + /* Inscription en bonne et due forme */ - free_mem: + storage->backends = realloc(storage->backends, ++storage->count * sizeof(storage_backend_t)); - free(data); + *backend = &storage->backends[storage->count - 1]; - restore: + (*backend)->name = strdup(name); - moved = lseek(backend->fd, current, SEEK_SET); - if (moved == ((off_t)-1)) - { - LOG_ERROR_N("lseek"); - goto exit; - } + (*backend)->filename = filename; + (*backend)->fd = fd; - result = status; + result = true; exit: @@ -602,7 +936,7 @@ static bool pack_storage_backend(const storage_backend_t *backend, packed_buffer /****************************************************************************** * * * Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * +* name = désignation d'un groupe d'objets à consulter. * * pos = tête de lecture avant écriture. * * * * Description : Charge un objet à partir de données rassemblées. * @@ -613,56 +947,44 @@ static bool pack_storage_backend(const storage_backend_t *backend, packed_buffer * * ******************************************************************************/ -GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const char *name, off64_t pos) +static GSerializableObject *g_object_storage_load_object_unlocked(GObjectStorage *storage, const char *name, off64_t pos) { GSerializableObject *result; /* Instance à retourner */ - bool status; /* Bilan d'une opération */ storage_backend_t *backend; /* Informations à consulter */ - packed_buffer_t pbuf; /* Tampon des données à lire */ off64_t new; /* Nouvelle position de lecture*/ + bool status; /* Bilan d'une opération */ result = NULL; - /* Chargement */ - - status = false; + assert(!g_mutex_trylock(&storage->mutex)); - g_mutex_lock(&storage->mutex); + /* Chargement */ backend = g_object_storage_find_backend(storage, name); + if (backend == NULL) goto exit; - if (backend != NULL) + new = lseek64(backend->fd, pos, SEEK_SET); + if (new == (off_t)-1) { - new = lseek64(backend->fd, pos, SEEK_SET); - - if (new == pos) - { - init_packed_buffer(&pbuf); - status = read_packed_buffer(&pbuf, backend->fd); - } - + LOG_ERROR_N("lseek64"); + goto exit; } - g_mutex_unlock(&storage->mutex); - - if (!status) - goto exit; + assert (new == pos); /* Phase de conversion */ - result = G_SERIALIZABLE_OBJECT(g_type_memory_create_object(storage->tpmem, &pbuf)); + result = G_SERIALIZABLE_OBJECT(g_type_memory_create_object_from_gtype(storage->tpmem, backend->fd)); if (result) { - status = g_serializable_object_load(result, storage, &pbuf); + status = g_serializable_object_load(result, storage, backend->fd); if (!status) g_clear_object(&result); } - exit_packed_buffer(&pbuf); - exit: return result; @@ -673,10 +995,10 @@ GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const /****************************************************************************** * * * Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * -* pbuf = zone tampon à parcourir. * +* name = désignation d'un groupe d'objets à consulter. * +* pos = tête de lecture avant écriture. * * * -* Description : Charge un objet interne à partir de données rassemblées. * +* Description : Charge un objet à partir de données rassemblées. * * * * Retour : Objet restauré en mémoire ou NULL en cas d'échec. * * * @@ -684,18 +1006,15 @@ GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const * * ******************************************************************************/ -GSerializableObject *g_object_storage_unpack_object(GObjectStorage *storage, const char *name, packed_buffer_t *pbuf) +GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const char *name, off64_t pos) { GSerializableObject *result; /* Instance à retourner */ - uint64_t pos; /* Localisation des données */ - bool status; /* Bilan d'une opération */ - result = NULL; + g_mutex_lock(&storage->mutex); - status = extract_packed_buffer(pbuf, &pos, sizeof(uint64_t), true); + result = g_object_storage_load_object_unlocked(storage, name, pos); - if (status) - result = g_object_storage_load_object(storage, name, pos); + g_mutex_unlock(&storage->mutex); return result; @@ -704,60 +1023,70 @@ GSerializableObject *g_object_storage_unpack_object(GObjectStorage *storage, con /****************************************************************************** * * -* Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * -* pbuf = zone tampon à parcourir. * -* expected = type d'objet attendu. * -* ... = élément restauré ou NULL en cas d'échec. [OUT] * +* Paramètres : storage = gestionnaire à manipuler. * +* fd = flux de données de l'objet courant. * +* name = désignation du groupe de l'objets à extraire. * * * -* Description : Charge un objet interne à partir de données rassemblées. * +* Description : Charge un objet interne à partir d'une référence embarquée. * * * -* Retour : Bilan de l'opération. * +* Retour : Objet restauré en mémoire ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ -bool g_object_storage_unpack_object_2(GObjectStorage *storage, const char *name, packed_buffer_t *pbuf, GType expected, ...) +GSerializableObject *g_object_storage_unpack_object(GObjectStorage *storage, int fd, const char *name) { - bool result; /* Bilan d'une opération */ - uint64_t pos; /* Localisation des données */ - GSerializableObject *instance; /* Objet rechargé à valider */ - va_list ap; /* Liste d'arguments variables */ - void **object; /* Lieu d'enregistrement final */ + GSerializableObject *result; /* Instance à retourner */ + storage_backend_t *backend; /* Informations à consulter */ + uleb128_t pos; /* Localisation des données */ + bool status; /* Bilan d'une opération */ + off64_t saved; /* Sauvegarde de position */ + off64_t new; /* Nouvelle position de lecture*/ - result = extract_packed_buffer(pbuf, &pos, sizeof(uint64_t), true); + result = NULL; - if (result) - { - if (pos == 0) - *object = NULL; + g_mutex_lock(&storage->mutex); - else - { - instance = g_object_storage_load_object(storage, name, pos); + /* Récupération de la position */ - result = G_TYPE_CHECK_INSTANCE_TYPE(instance, expected); + backend = g_object_storage_find_backend(storage, name); + if (backend == NULL) goto exit; - if (result) - { - va_start(ap, expected); + status = load_uleb128(&pos, backend->fd); + if (!status) goto exit; - object = va_arg(ap, void **); + saved = lseek64(backend->fd, 0, SEEK_CUR); + if (saved == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + goto exit; + } - *object = instance; + /* Chargement */ - va_end(ap); + result = g_object_storage_load_object_unlocked(storage, name, pos); - } + if (result == NULL) goto exit; - else - g_clear_object(&instance); + /* Restauration de la position courante */ - } + new = lseek64(backend->fd, saved, SEEK_SET); + if (new == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + + g_clear_object(&result); + goto exit; } + assert (new == saved); + + exit: + + g_mutex_unlock(&storage->mutex); + return result; } @@ -766,7 +1095,7 @@ bool g_object_storage_unpack_object_2(GObjectStorage *storage, const char *name, /****************************************************************************** * * * Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * +* name = désignation d'un groupe d'objets, nouveau ou non. * * object = objet sérialisable à traiter. * * pos = tête de lecture avant écriture. [OUT] * * * @@ -781,22 +1110,9 @@ bool g_object_storage_unpack_object_2(GObjectStorage *storage, const char *name, bool g_object_storage_store_object(GObjectStorage *storage, const char *name, const GSerializableObject *object, off64_t *pos) { bool result; /* Bilan à retourner */ - packed_buffer_t pbuf; /* Tampon des données à écrire */ storage_backend_t *backend; /* Informations à consulter */ off64_t tmp; /* Conservation éphémère */ - /* Phase de conversion */ - - init_packed_buffer(&pbuf); - - result = g_type_memory_store_object_gtype(storage->tpmem, G_OBJECT(object), &pbuf); - if (!result) goto exit; - - result = g_serializable_object_store(object, storage, &pbuf); - if (!result) goto exit; - - /* Enregistrement */ - result = false; g_mutex_lock(&storage->mutex); @@ -814,54 +1130,18 @@ bool g_object_storage_store_object(GObjectStorage *storage, const char *name, co *pos = lseek64(backend->fd, 0, SEEK_CUR); if (*pos != (off64_t)-1) - result = write_packed_buffer(&pbuf, backend->fd); - - } - - g_mutex_unlock(&storage->mutex); - - /* Sortie propre */ - - exit: - - exit_packed_buffer(&pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * -* pbuf = zone tampon à remplir. * -* * -* Description : Sauvegarde un object interne sous forme de données. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_object_storage_pack_object(GObjectStorage *storage, const char *name, const GSerializableObject *object, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - off64_t pos; /* Localisation des données */ - - if (object == NULL) - result = extend_packed_buffer(pbuf, (uint64_t []){ 0 }, sizeof(uint64_t), true); + { + result = g_type_memory_store_object_gtype(storage->tpmem, G_OBJECT(object), backend->fd); - else - { - result = g_object_storage_store_object(storage, name, object, &pos); + if (result) + result = g_serializable_object_store(object, storage, backend->fd); - if (result) - result = extend_packed_buffer(pbuf, (uint64_t []){ pos }, sizeof(uint64_t), true); + } } + g_mutex_unlock(&storage->mutex); + return result; } diff --git a/src/analysis/storage/storage.h b/src/glibext/storage.h index cc0caad..ea06ed4 100644 --- a/src/analysis/storage/storage.h +++ b/src/glibext/storage.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * storage.h - prototypes pour la conservation sur disque d'objets construits * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,69 +21,61 @@ */ -#ifndef _ANALYSIS_STORAGE_STORAGE_H -#define _ANALYSIS_STORAGE_STORAGE_H +#ifndef _GLIBEXT_STORAGE_H +#define _GLIBEXT_STORAGE_H -#include <glib-object.h> -#include <stdbool.h> +#include <stdint.h> +#include "helpers.h" #include "serialize.h" -#include "tpmem.h" -#define G_TYPE_OBJECT_STORAGE g_object_storage_get_type() -#define G_OBJECT_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_OBJECT_STORAGE, GObjectStorage)) -#define G_IS_OBJECT_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_OBJECT_STORAGE)) -#define G_OBJECT_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_OBJECT_STORAGE, GObjectStorageClass)) -#define G_IS_OBJECT_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_OBJECT_STORAGE)) -#define G_OBJECT_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_OBJECT_STORAGE, GObjectStorageClass)) +#define G_TYPE_OBJECT_STORAGE (g_object_storage_get_type()) +DECLARE_GTYPE(GObjectStorage, g_object_storage, G, OBJECT_STORAGE); -/* Définition d'une conservation d'objets construits (instance) */ -typedef struct _GObjectStorage GObjectStorage; - -/* Définition d'une conservation d'objets construits (classe) */ -typedef struct _GObjectStorageClass GObjectStorageClass; - - -/* Indique le type défini pour une conservation d'objets construits. */ -GType g_object_storage_get_type(void); /* Crée le support d'une conservation d'objets en place. */ -GObjectStorage *g_object_storage_new(const char *); - -#define get_storage_linked_format(s) \ - ({ \ - void*__result; \ - __result = g_object_get_data(G_OBJECT(s), "format"); \ - g_object_ref(G_OBJECT(__result)); \ - __result; \ - }) +GObjectStorage *g_object_storage_new(const char *, uint8_t, const char *); /* Charge le support d'une conservation d'objets en place. */ -GObjectStorage *g_object_storage_load(packed_buffer_t *); +GObjectStorage *g_object_storage_load(const char *); /* Sauvegarde le support d'une conservation d'objets en place. */ -bool g_object_storage_store(GObjectStorage *, packed_buffer_t *); +bool g_object_storage_store(GObjectStorage *, const char *); /* Charge un objet à partir de données rassemblées. */ GSerializableObject *g_object_storage_load_object(GObjectStorage *, const char *, off64_t); -/* Charge un objet interne à partir de données rassemblées. */ -GSerializableObject *g_object_storage_unpack_object(GObjectStorage *, const char *, packed_buffer_t *); +/* Charge un objet interne à partir d'une référence embarquée. */ +GSerializableObject *g_object_storage_unpack_object(GObjectStorage *, int, const char *); /* Sauvegarde un object sous forme de données rassemblées. */ bool g_object_storage_store_object(GObjectStorage *, const char *, const GSerializableObject *, off64_t *); -/* Charge un objet interne à partir de données rassemblées. */ -bool g_object_storage_unpack_object_2(GObjectStorage *, const char *, packed_buffer_t *, GType, ...); -/* Sauvegarde un object interne sous forme de données. */ -bool g_object_storage_pack_object(GObjectStorage *, const char *, const GSerializableObject *, packed_buffer_t *); + + +#if 0 + +/** + * TODO : REMME ? + */ + +#define get_storage_linked_format(s) \ + ({ \ + void*__result; \ + __result = g_object_get_data(G_OBJECT(s), "format"); \ + g_object_ref(G_OBJECT(__result)); \ + __result; \ + }) + +#endif + -#endif /* _ANALYSIS_STORAGE_STORAGE_H */ +#endif /* _GLIBEXT_STORAGE_H */ diff --git a/src/glibext/strbuilder-int.h b/src/glibext/strbuilder-int.h new file mode 100644 index 0000000..d74e65c --- /dev/null +++ b/src/glibext/strbuilder-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder-int.h - définitions internes propres aux éléments exportant une chaîne de caractères + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_STRBUILDER_INT_H +#define _GLIBEXT_STRBUILDER_INT_H + + +#include "strbuilder.h" + + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +typedef bool (* strbuilder_to_string_fc) (const GStringBuilder *, unsigned int, sized_binary_t *); + + +/* Instance d'objet visant à être unique (interface) */ +struct _GStringBuilderInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + strbuilder_to_string_fc to_string; /* Conversion en chaîne */ + +}; + + + +#endif /* _GLIBEXT_STRBUILDER_INT_H */ diff --git a/src/glibext/strbuilder.c b/src/glibext/strbuilder.c new file mode 100644 index 0000000..e48674c --- /dev/null +++ b/src/glibext/strbuilder.c @@ -0,0 +1,89 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.c - exportation d'une chaîne de caractères depuis un élément + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "strbuilder.h" + + +#include "strbuilder-int.h" + + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void g_string_builder_default_init(GStringBuilderInterface *); + + + +/* Détermine le type d'une interface pour la constitution d'objets uniques. */ +G_DEFINE_INTERFACE(GStringBuilder, g_string_builder, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_string_builder_default_init(GStringBuilderInterface *iface) +{ + iface->to_string = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * +* * +******************************************************************************/ + +bool g_string_builder_to_string(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + GStringBuilderInterface *iface; /* Interface utilisée */ + + iface = G_STRING_BUILDER_GET_IFACE(builder); + + if (iface->to_string == NULL) + result = false; + + else + result = iface->to_string(builder, flags, out); + + return result; + +} diff --git a/src/glibext/strbuilder.h b/src/glibext/strbuilder.h new file mode 100644 index 0000000..b6422f7 --- /dev/null +++ b/src/glibext/strbuilder.h @@ -0,0 +1,46 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.h - prototypes pour l'exportation d'une chaîne de caractères depuis un élément + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_STRBUILDER_H +#define _GLIBEXT_STRBUILDER_H + + +#include <stdbool.h> + + +#include "helpers.h" +#include "../common/szbin.h" + + + +#define G_TYPE_STRING_BUILDER (g_string_builder_get_type()) + +DECLARE_INTERFACE(GStringBuilder, g_string_builder, G, STRING_BUILDER); + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +bool g_string_builder_to_string(const GStringBuilder *, unsigned int, sized_binary_t *); + + + +#endif /* _GLIBEXT_STRBUILDER_H */ diff --git a/src/glibext/tpmem-int.h b/src/glibext/tpmem-int.h new file mode 100644 index 0000000..b1b7eec --- /dev/null +++ b/src/glibext/tpmem-int.h @@ -0,0 +1,78 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tpmem-int.h - définitions internes propres à la mémorisation des types d'objets mis en cache + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_TPMEM_INT_H +#define _GLIBEXT_TPMEM_INT_H + + +#include "tpmem.h" + + +/* Conservation d'une référence sur un type */ +typedef struct _gtype_ref_info_t +{ + GType gtype; /* Type pour la GLib */ + gpointer gclass; /* Lien vers sa classe */ + + /** + * La GLib n'est pas très claire sur la taille de GType comme le montre le + * code issu de <sources>/gobject/gtype.h : + * + * #if GLIB_SIZEOF_SIZE_T != GLIB_SIZEOF_LONG || !defined __cplusplus + * typedef gsize GType; + * #else // for historic reasons, C++ links against gulong GTypes + * typedef gulong GType; + * #endif + * + * Et : + * + * typedef unsigned $glib_size_type_define gsize; + * + * On prend donc le parti de conserver ces types sous forme de valeurs 64 bits + * lors des enregistrements. + */ + +} gtype_ref_info_t; + + +/* Définition d'une mémoire de types d'objets (instance) */ +struct _GTypeMemory +{ + GObject parent; /* A laisser en premier */ + + gtype_ref_info_t *gtypes; /* Types des objets reconnus */ + size_t count; /* Quantité de ces objets */ + GMutex mutex; /* Contrôle d'accès à la liste */ + +}; + +/* Définition d'une mémoire de types d'objets (classe) */ +struct _GTypeMemoryClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GLIBEXT_TPMEM_INT_H */ diff --git a/src/analysis/storage/tpmem.c b/src/glibext/tpmem.c index 0703aeb..14b5e33 100644 --- a/src/analysis/storage/tpmem.c +++ b/src/glibext/tpmem.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * tpmem.c - mémorisation des types d'objets mis en cache * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,59 +25,14 @@ #include <assert.h> -#include <stdint.h> -#include "../db/misc/rlestr.h" -#include "../../arch/operands/target.h" -#include "../../core/logs.h" +#include "tpmem-int.h" +#include "../common/szbin.h" +#include "../core/logs.h" -/* Conservation d'une référence sur un type */ -typedef struct _gtype_ref_info_t -{ - GType gtype; /* Type pour la GLib */ - gpointer gclass; /* Lien vers sa classe */ - - /** - * La GLib n'est pas très claire sur la taille de GType : - * - * #if GLIB_SIZEOF_SIZE_T != GLIB_SIZEOF_LONG || !defined __cplusplus - * typedef gsize GType; - * #else // for historic reasons, C++ links against gulong GTypes - * typedef gulong GType; - * #endif - * - * Et : - * - * typedef unsigned $glib_size_type_define gsize; - * - * On prend donc le parti de conserver ces types sous forme de valeurs 64 bits - * lors des enregistrements. - */ - -} gtype_ref_info_t; - -/* Définition d'une mémoire de types d'objets (instance) */ -struct _GTypeMemory -{ - GObject parent; /* A laisser en premier */ - - gtype_ref_info_t *gtypes; /* Types des objets reconnus */ - size_t count; /* Quantité de ces objets */ - GMutex mutex; /* Contrôle d'accès à la liste */ - -}; - -/* Définition d'une mémoire de types d'objets (classe) */ -struct _GTypeMemoryClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; - - /* Initialise la classe des mémoires de types d'objets. */ static void g_type_memory_class_init(GTypeMemoryClass *); @@ -85,10 +40,10 @@ static void g_type_memory_class_init(GTypeMemoryClass *); static void g_type_memory_init(GTypeMemory *); /* Supprime toutes les références externes. */ -static void g_type_memory_dispose(GTypeMemory *); +static void g_type_memory_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_type_memory_finalize(GTypeMemory *); +static void g_type_memory_finalize(GObject *); @@ -114,8 +69,8 @@ static void g_type_memory_class_init(GTypeMemoryClass *klass) object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_type_memory_dispose; - object->finalize = (GObjectFinalizeFunc)g_type_memory_finalize; + object->dispose = g_type_memory_dispose; + object->finalize = g_type_memory_finalize; } @@ -143,7 +98,7 @@ static void g_type_memory_init(GTypeMemory *tpmem) /****************************************************************************** * * -* Paramètres : tpmem = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -153,10 +108,13 @@ static void g_type_memory_init(GTypeMemory *tpmem) * * ******************************************************************************/ -static void g_type_memory_dispose(GTypeMemory *tpmem) +static void g_type_memory_dispose(GObject *object) { + GTypeMemory *tpmem; /* Version spécialisée */ uint64_t i; /* Boucle de parcours */ + tpmem = G_TYPE_MEMORY(object); + g_mutex_lock(&tpmem->mutex); for (i = 0; i < tpmem->count; i++) @@ -167,14 +125,14 @@ static void g_type_memory_dispose(GTypeMemory *tpmem) g_mutex_clear(&tpmem->mutex); - G_OBJECT_CLASS(g_type_memory_parent_class)->dispose(G_OBJECT(tpmem)); + G_OBJECT_CLASS(g_type_memory_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : tpmem = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -184,12 +142,16 @@ static void g_type_memory_dispose(GTypeMemory *tpmem) * * ******************************************************************************/ -static void g_type_memory_finalize(GTypeMemory *tpmem) +static void g_type_memory_finalize(GObject *object) { + GTypeMemory *tpmem; /* Version spécialisée */ + + tpmem = G_TYPE_MEMORY(object); + if (tpmem->gtypes != NULL) free(tpmem->gtypes); - G_OBJECT_CLASS(g_type_memory_parent_class)->finalize(G_OBJECT(tpmem)); + G_OBJECT_CLASS(g_type_memory_parent_class)->finalize(object); } @@ -220,7 +182,7 @@ GTypeMemory *g_type_memory_new(void) /****************************************************************************** * * * Paramètres : tpmem = mémoire à compléter. * -* pbuf = zone tampon à lire. * +* fd = flux ouvert en lecture. * * * * Description : Apprend tous les types mémorisés dans un tampon. * * * @@ -230,14 +192,14 @@ GTypeMemory *g_type_memory_new(void) * * ******************************************************************************/ -bool g_type_memory_load_types(GTypeMemory *tpmem, packed_buffer_t *pbuf) +bool g_type_memory_load(GTypeMemory *tpmem, int fd) { bool result; /* Bilan à enregistrer */ uleb128_t count; /* Nombre d'éléments détectés */ uleb128_t i; /* Boucle de parcours */ - rle_string str; /* Chaîne à charger */ + sized_binary_t str; /* Chaîne à charger */ - result = unpack_uleb128(&count, pbuf); + result = load_uleb128(&count, fd); if (result) { @@ -248,35 +210,27 @@ bool g_type_memory_load_types(GTypeMemory *tpmem, packed_buffer_t *pbuf) assert(tpmem->gtypes == NULL); tpmem->gtypes = calloc(count, sizeof(gtype_ref_info_t)); - setup_empty_rle_string(&str); - for (i = 0; i < tpmem->count && result; i++) { - result = unpack_rle_string(&str, pbuf); + result = load_sized_binary_as_string(&str, fd); if (!result) break; - if (get_rle_string(&str) == NULL) - { - exit_rle_string(&str); - break; - } - - tpmem->gtypes[i].gtype = g_type_from_name(get_rle_string(&str)); + tpmem->gtypes[i].gtype = g_type_from_name(str.data); result = (tpmem->gtypes[i].gtype != 0); if (!result) - log_variadic_message(LMT_ERROR, "Unknown type: '%s'", get_rle_string(&str)); + log_variadic_message(LMT_ERROR, "Unknown type: '%s'", str.data); else tpmem->gtypes[i].gclass = g_type_class_ref(tpmem->gtypes[i].gtype); - exit_rle_string(&str); + exit_sized_binary(&str); } - } + g_mutex_unlock(&tpmem->mutex); - g_mutex_unlock(&tpmem->mutex); + } return result; @@ -285,38 +239,40 @@ bool g_type_memory_load_types(GTypeMemory *tpmem, packed_buffer_t *pbuf) /****************************************************************************** * * -* Paramètres : tpmem = mémoire à manipuler. * -* pbuf = zone tampon à venir lire. * +* Paramètres : tpmem = mémoire à consulter. * +* fd = flux ouvert en écriture. * * * -* Description : Crée une nouvelle instance d'objet à partir de son type. * +* Description : Enregistre tous les types mémorisés dans un tampon. * * * -* Retour : Instance issue de l'opération ou NULL. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -GObject *g_type_memory_create_object(GTypeMemory *tpmem, packed_buffer_t *pbuf) +bool g_type_memory_store(GTypeMemory *tpmem, int fd) { - GObject *result; /* Nouvelle instance à renvoyer*/ - uleb128_t index; /* Indice du point d'insertion */ - bool status; /* Bilan d'une récupération */ + bool result; /* Bilan à enregistrer */ + uint64_t i; /* Boucle de parcours */ + const gchar *name; /* Désignation d'un type */ + sized_binary_t str; /* Chaîne à conserver */ - result = NULL; + g_mutex_lock(&tpmem->mutex); - status = unpack_uleb128(&index, pbuf); + result = store_uleb128((uleb128_t []){ tpmem->count }, fd); - if (status) + for (i = 0; i < tpmem->count && result; i++) { - g_mutex_lock(&tpmem->mutex); + name = g_type_name(tpmem->gtypes[i].gtype); - if (index < tpmem->count) - result = g_object_new(tpmem->gtypes[index].gtype, NULL); + setup_sized_binary_from_static_string(&str, name); - g_mutex_unlock(&tpmem->mutex); + store_sized_binary_as_string(&str, fd); } + g_mutex_unlock(&tpmem->mutex); + return result; } @@ -325,60 +281,35 @@ GObject *g_type_memory_create_object(GTypeMemory *tpmem, packed_buffer_t *pbuf) /****************************************************************************** * * * Paramètres : tpmem = mémoire à manipuler. * -* obj = instance dont le type est à mémoriser. * -* pbuf = zone tampon à remplir. [OUT] * +* fd = flux ouvert en lecture. * * * -* Description : Sauvegarde le type d'un objet instancié. * +* Description : Crée une nouvelle instance d'objet à partir de son type. * * * -* Retour : Bilan de l'opération. * +* Retour : Instance issue de l'opération ou NULL. * * * * Remarques : - * * * ******************************************************************************/ -bool g_type_memory_store_object_gtype(GTypeMemory *tpmem, GObject *obj, packed_buffer_t *pbuf) +GObject *g_type_memory_create_object_from_gtype(GTypeMemory *tpmem, int fd) { - bool result; /* Bilan à retourner */ - GType gtype; /* Type à enregistrer */ - size_t index; /* Indice du point d'insertion */ - - gtype = G_TYPE_FROM_INSTANCE(obj); + GObject *result; /* Nouvelle instance à renvoyer*/ + uleb128_t index; /* Indice du point d'insertion */ + bool status; /* Bilan d'une récupération */ - /** - * Pour quelques explications sur l'esquive suivante, se rapporter aux - * commentaires de g_target_operand_unserialize(). - * - * Dans la situation présente, on ne doit pas enregistrer le type dans le tampon, - * car l'opérande va relancer l'opération entière (avec un opérande temporaire), - * ce qui conduirait à l'enregistrement de deux types successifs dans les données. - */ + result = NULL; - if (gtype == G_TYPE_TARGET_OPERAND) - result = true; + status = load_uleb128(&index, fd); - else + if (status) { g_mutex_lock(&tpmem->mutex); - for (index = 0; index < tpmem->count; index++) - if (tpmem->gtypes[index].gtype == gtype) - break; - - if (index == tpmem->count) - { - tpmem->gtypes = realloc(tpmem->gtypes, ++tpmem->count * sizeof(gtype_ref_info_t)); - - assert(tpmem->count > 0); - - tpmem->gtypes[index].gtype = gtype; - tpmem->gtypes[index].gclass = g_type_class_ref(gtype); - - } + if (index < tpmem->count) + result = g_object_new(tpmem->gtypes[index].gtype, NULL); g_mutex_unlock(&tpmem->mutex); - result = pack_uleb128((uleb128_t []){ index }, pbuf); - } return result; @@ -388,10 +319,11 @@ bool g_type_memory_store_object_gtype(GTypeMemory *tpmem, GObject *obj, packed_b /****************************************************************************** * * -* Paramètres : tpmem = mémoire à consulter. * -* pbuf = zone tampon à remplir. [OUT] * +* Paramètres : tpmem = mémoire à manipuler. * +* obj = instance dont le type est à mémoriser. * +* fd = flux ouvert en écriture. * * * -* Description : Enregistre tous les types mémorisés dans un tampon. * +* Description : Sauvegarde le type d'un objet instancié. * * * * Retour : Bilan de l'opération. * * * @@ -399,31 +331,33 @@ bool g_type_memory_store_object_gtype(GTypeMemory *tpmem, GObject *obj, packed_b * * ******************************************************************************/ -bool g_type_memory_store_types(GTypeMemory *tpmem, packed_buffer_t *pbuf) +bool g_type_memory_store_object_gtype(GTypeMemory *tpmem, GObject *obj, int fd) { - bool result; /* Bilan à enregistrer */ - uint64_t i; /* Boucle de parcours */ - const gchar *name; /* Désignation d'un type */ - rle_string str; /* Chaîne à conserver */ + bool result; /* Bilan à retourner */ + GType gtype; /* Type à enregistrer */ + size_t index; /* Indice du point d'insertion */ + + gtype = G_TYPE_FROM_INSTANCE(obj); g_mutex_lock(&tpmem->mutex); - result = pack_uleb128((uleb128_t []){ tpmem->count }, pbuf); + for (index = 0; index < tpmem->count; index++) + if (tpmem->gtypes[index].gtype == gtype) + break; - for (i = 0; i < tpmem->count && result; i++) + if (index == tpmem->count) { - name = g_type_name(tpmem->gtypes[i].gtype); - - init_static_rle_string(&str, name); + tpmem->gtypes = realloc(tpmem->gtypes, ++tpmem->count * sizeof(gtype_ref_info_t)); - result = pack_rle_string(&str, pbuf); - - exit_rle_string(&str); + tpmem->gtypes[index].gtype = gtype; + tpmem->gtypes[index].gclass = g_type_class_ref(gtype); } g_mutex_unlock(&tpmem->mutex); + result = store_uleb128((uleb128_t []){ index }, fd); + return result; } diff --git a/src/glibext/tpmem.h b/src/glibext/tpmem.h new file mode 100644 index 0000000..ccb8323 --- /dev/null +++ b/src/glibext/tpmem.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tpmem.h - prototypes pour la mémorisation des types d'objets mis en cache + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_TPMEM_H +#define _GLIBEXT_TPMEM_H + + +#include <stdbool.h> + + +#include "helpers.h" + + + +#define G_TYPE_TYPE_MEMORY (g_type_memory_get_type()) + +DECLARE_GTYPE(GTypeMemory, g_type_memory, G, TYPE_MEMORY); + + +/* Crée une mémoire pour types d'objets. */ +GTypeMemory *g_type_memory_new(void); + +/* Apprend tous les types mémorisés dans un tampon. */ +bool g_type_memory_load(GTypeMemory *, int); + +/* Enregistre tous les types mémorisés dans un tampon. */ +bool g_type_memory_store(GTypeMemory *, int); + +/* Crée une nouvelle instance d'objet à partir de son type. */ +GObject *g_type_memory_create_object_from_gtype(GTypeMemory *, int); + +/* Sauvegarde le type d'un objet instancié. */ +bool g_type_memory_store_object_gtype(GTypeMemory *, GObject *, int); + + + +#endif /* _GLIBEXT_TPMEM_H */ diff --git a/src/glibext/widthtracker.c b/src/glibext/widthtracker.c index 7e06578..0a3dbe6 100644 --- a/src/glibext/widthtracker.c +++ b/src/glibext/widthtracker.c @@ -330,13 +330,17 @@ void g_width_tracker_set_column_min_width(GWidthTracker *tracker, size_t col, in void g_width_tracker_compute_width(const GWidthTracker *tracker, int *width) { + size_t col_count; /* Nombre maximum de colonnes */ size_t i; /* Boucle de parcours */ *width = 0; - // FIX access to col_count - for (i = 0; i < tracker->col_count; i++) + + col_count = tracker->opt_count + tracker->reg_count; + + + for (i = 0; i < col_count; i++) *width += tracker->min_widths[i]; } diff --git a/src/glibext/work-int.h b/src/glibext/work-int.h index 4f84e86..58293e5 100644 --- a/src/glibext/work-int.h +++ b/src/glibext/work-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed-int.h - définitions internes pour la gestion des travaux différés + * work-int.h - définitions internes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,27 +21,26 @@ */ -#ifndef _GLIBEXT_DELAYED_INT_H -#define _GLIBEXT_DELAYED_INT_H +#ifndef _GLIBEXT_WORK_INT_H +#define _GLIBEXT_WORK_INT_H -#include "delayed.h" +#include "work.h" -#include "notifier.h" -#include "../common/dllist.h" +#include <stdbool.h> +#include "../common/dllist.h" -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ /* Traite un travail programmé. */ -typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *); +typedef void (* run_work_fc) (GGenericWork *); /* Travail différé (instance) */ -struct _GDelayedWork +struct _GGenericWork { GObject parent; /* A laisser en premier */ @@ -54,22 +53,18 @@ struct _GDelayedWork }; /* Travail différé (classe) */ -struct _GDelayedWorkClass +struct _GGenericWorkClass { GObjectClass parent; /* A laisser en premier */ - run_task_fc run; /* Traitement externalisé */ + run_work_fc run; /* Traitement externalisé */ /* Signaux */ - void (* work_completed) (GDelayedWork *); + void (* work_completed) (GGenericWork *); }; -#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link) -#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link) - - -#endif /* _GLIBEXT_DELAYED_INT_H */ +#endif /* _GLIBEXT_WORK_INT_H */ diff --git a/src/glibext/work.c b/src/glibext/work.c index 6b5ac35..218fcf8 100644 --- a/src/glibext/work.c +++ b/src/glibext/work.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.c - gestion des travaux différés + * work.c - gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,178 +21,29 @@ */ -#include "delayed.h" +#include "work.h" -#include <assert.h> -#include <inttypes.h> -#include <malloc.h> -#include <stdio.h> -#include <string.h> +#include "work-int.h" -#include "delayed-int.h" -#include "../core/nproc.h" -#ifdef INCLUDE_GTK_SUPPORT -# include "../gui/core/global.h" -#endif - - - -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - /* Initialise la classe des travaux différés. */ -static void g_delayed_work_class_init(GDelayedWorkClass *); +static void g_generic_work_class_init(GGenericWorkClass *); /* Initialise une instance de travail différé. */ -static void g_delayed_work_init(GDelayedWork *); +static void g_generic_work_init(GGenericWork *); /* Supprime toutes les références externes. */ -static void g_delayed_work_dispose(GDelayedWork *); +static void g_generic_work_dispose(GGenericWork *); /* Procède à la libération totale de la mémoire. */ -static void g_delayed_work_finalize(GDelayedWork *); - -/* Mène l'opération programmée. */ -static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *); - - - -/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */ - - -#define G_TYPE_WORK_GROUP g_work_group_get_type() -#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup)) -#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type())) -#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass)) -#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP)) -#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass)) - - -/* File de traitement pour un type donné (instance) */ -typedef struct _GWorkGroup -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t id; /* Identifiant de travaux menés*/ - - GDelayedWork *works; /* Tâches à mener à bien */ - GMutex mutex; /* Verrou pour l'accès */ - GCond cond; /* Réveil pour un traitement */ - GCond wait_cond; /* Réveil d'attente de fin */ - gint pending; /* Tâches en cours d'exécution */ - - GThread **threads; /* Procédure de traitement */ - guint threads_count; /* Nombre de procédures */ - bool force_exit; /* Procédure d'arrêt */ - - wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */ - void *data; /* Données à associer */ - -} GWorkGroup; - -/* File de traitement pour un type donné (classe) */ -typedef struct _GWorkGroupClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GWorkGroupClass; - - -/* Indique le type défini pour les groupes de travail. */ -static GType g_work_group_get_type(void); - -/* Initialise la classe des groupes de travail. */ -static void g_work_group_class_init(GWorkGroupClass *); - -/* Initialise une instance de groupe de travail. */ -static void g_work_group_init(GWorkGroup *); - -/* Supprime toutes les références externes. */ -static void g_work_group_dispose(GWorkGroup *); - -/* Procède à la libération totale de la mémoire. */ -static void g_work_group_finalize(GWorkGroup *); - -/* Crée un nouveau thread dédié à un type de travaux donné. */ -static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *); - -/* Fournit l'identifiant associé à un groupe de travail. */ -static wgroup_id_t g_work_group_get_id(const GWorkGroup *); - -/* Place une nouvelle tâche en attente dans une file dédiée. */ -static void g_work_group_schedule(GWorkGroup *, GDelayedWork *); - -/* Assure le traitement en différé. */ -static void *g_work_group_process(GWorkGroup *); - -/* Détermine si le groupe est vide de toute programmation. */ -static bool g_work_group_is_empty(GWorkGroup *); - -/* Attend que toutes les tâches d'un groupe soient traitées. */ -static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *); - -/* Modifie les conditions d'attente des fins d'exécutions. */ -static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *); - -/* Force un réveil d'une attente en cours pour la confirmer. */ -static void g_work_group_wake_up_waiters(GWorkGroup *); - - +static void g_generic_work_finalize(GGenericWork *); -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -/* Gestionnaire des travaux différés (instance) */ -struct _GWorkQueue -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t generator; /* Générateur d'identifiants */ - - GWorkGroup **groups; /* Files de traitement */ - size_t groups_count; /* Nombre de files internes */ - GMutex mutex; /* Verrou pour l'accès */ - GCond wait_all; /* Réveil d'attente globale */ - -}; - -/* Gestionnaire des travaux différés (classe) */ -struct _GWorkQueueClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; - - -/* Initialise la classe des travaux différés. */ -static void g_work_queue_class_init(GWorkQueueClass *); - -/* Initialise une instance de gestionnaire de travaux différés. */ -static void g_work_queue_init(GWorkQueue *); - -/* Supprime toutes les références externes. */ -static void g_work_queue_dispose(GWorkQueue *); - -/* Procède à la libération totale de la mémoire. */ -static void g_work_queue_finalize(GWorkQueue *); - -/* Donne l'assurance de l'existence d'un groupe de travail. */ -static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *); - -/* Fournit le groupe de travail correspondant à un identifiant. */ -static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t); - - - -/* ---------------------------------------------------------------------------------- */ -/* TACHE DIFFEREE DANS LE TEMPS */ -/* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour les travaux différés. */ -G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT); +G_DEFINE_TYPE(GGenericWork, g_generic_work, G_TYPE_OBJECT); /****************************************************************************** @@ -207,19 +58,19 @@ G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT); * * ******************************************************************************/ -static void g_delayed_work_class_init(GDelayedWorkClass *klass) +static void g_generic_work_class_init(GGenericWorkClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose; - object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize; + object->dispose = (GObjectFinalizeFunc/* ! */)g_generic_work_dispose; + object->finalize = (GObjectFinalizeFunc)g_generic_work_finalize; g_signal_new("work-completed", - G_TYPE_DELAYED_WORK, + G_TYPE_GENERIC_WORK, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GDelayedWorkClass, work_completed), + G_STRUCT_OFFSET(GGenericWorkClass, work_completed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -239,8 +90,10 @@ static void g_delayed_work_class_init(GDelayedWorkClass *klass) * * ******************************************************************************/ -static void g_delayed_work_init(GDelayedWork *work) +static void g_generic_work_init(GGenericWork *work) { + DL_LIST_ITEM_INIT(&work->link); + work->completed = false; g_mutex_init(&work->mutex); g_cond_init(&work->cond); @@ -260,12 +113,12 @@ static void g_delayed_work_init(GDelayedWork *work) * * ******************************************************************************/ -static void g_delayed_work_dispose(GDelayedWork *work) +static void g_generic_work_dispose(GGenericWork *work) { g_mutex_clear(&work->mutex); g_cond_clear(&work->cond); - G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work)); + G_OBJECT_CLASS(g_generic_work_parent_class)->dispose(G_OBJECT(work)); } @@ -282,281 +135,19 @@ static void g_delayed_work_dispose(GDelayedWork *work) * * ******************************************************************************/ -static void g_delayed_work_finalize(GDelayedWork *work) -{ - G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à effectuer. * -* status = barre de statut à tenir informée. * -* * -* Description : Mène l'opération programmée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status) -{ - G_DELAYED_WORK_GET_CLASS(work)->run(work, status); - - g_mutex_lock(&work->mutex); - - work->completed = true; - - g_cond_signal(&work->cond); - g_mutex_unlock(&work->mutex); - - g_signal_emit_by_name(work, "work-completed"); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à surveiller. * -* * -* Description : Attend la fin de l'exécution d'une tâche donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_delayed_work_wait_for_completion(GDelayedWork *work) -{ - g_mutex_lock(&work->mutex); - - while (!work->completed) - g_cond_wait(&work->cond, &work->mutex); - - g_mutex_unlock(&work->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* THREADS DES TRAITEMENTS DEDIES */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour les groupes de travail. */ -G_DEFINE_TYPE(GWorkGroup, g_work_group, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des groupes de travail. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_class_init(GWorkGroupClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_work_group_dispose; - object->finalize = (GObjectFinalizeFunc)g_work_group_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : group = instance à initialiser. * -* * -* Description : Initialise une instance de groupe de travail. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_init(GWorkGroup *group) -{ - group->works = NULL; - - g_mutex_init(&group->mutex); - g_cond_init(&group->cond); - g_cond_init(&group->wait_cond); - - g_atomic_int_set(&group->pending, 0); - - group->threads = NULL; - group->threads_count = 0; - group->force_exit = false; - - group->callback = NULL; - group->data = NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_dispose(GWorkGroup *group) -{ - guint i; /* Boucle de parcours */ - GDelayedWork *work; /* Travail à oublier */ - - group->force_exit = true; - - /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). - */ - - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->cond); - - g_mutex_unlock(&group->mutex); - - for (i = 0; i < group->threads_count; i++) - g_thread_join(group->threads[i]); - - while (!dl_list_empty(group->works)) - { - work = group->works; - delayed_work_list_del(work, &group->works); - - g_object_unref(G_OBJECT(work)); - - } - - g_mutex_clear(&group->mutex); - g_cond_clear(&group->cond); - g_cond_clear(&group->wait_cond); - - G_OBJECT_CLASS(g_work_group_parent_class)->dispose(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : group = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_finalize(GWorkGroup *group) -{ - if (group->threads != NULL) - free(group->threads); - - G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : id = identifiant accordé au nouveau groupe. * -* count = quantité de threads à allouer. * -* * -* Description : Crée un nouveau thread dédié à un type de travaux donné. * -* * -* Retour : Structure associée au thread mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count) -{ - GWorkGroup *result; /* Traiteur à retourner */ - guint i; /* Boucle de parcours */ - char name[16]; /* Désignation humaine */ - - result = g_object_new(G_TYPE_WORK_GROUP, NULL); - - result->id = id; - - result->threads_count = get_max_online_threads(); - - if (count != NULL && *count < result->threads_count) - result->threads_count = *count; - - result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *)); - - for (i = 0; i < result->threads_count; i++) - { - snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i); - - result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result); - if (!result->threads[i]) - goto start_error; - - } - - start_error: - - result->threads_count = i; - - assert(i > 0); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à mener. * -* * -* Description : Fournit l'identifiant associé à un groupe de travail. * -* * -* Retour : Identifiant unique attribué au groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) +static void g_generic_work_finalize(GGenericWork *work) { - return group->id; + G_OBJECT_CLASS(g_generic_work_parent_class)->finalize(G_OBJECT(work)); } /****************************************************************************** * * -* Paramètres : group = gestionnaire des actions à mener. * -* work = nouvelle tâche à programmer, puis effectuer. * +* Paramètres : work = travail à traiter. * +* list = ensemble de travaux à considérer. [OUT] * * * -* Description : Place une nouvelle tâche en attente dans une file dédiée. * +* Description : Intègre un travail dans une liste de tâches à effectuer. * * * * Retour : - * * * @@ -564,161 +155,19 @@ static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) +void g_generic_work_add_to_list(GGenericWork *work, GGenericWork **list) { - g_mutex_lock(&group->mutex); - - g_atomic_int_inc(&group->pending); - - delayed_work_list_add_tail(work, &group->works); - - g_cond_signal(&group->cond); - - g_mutex_unlock(&group->mutex); + dl_list_add_tail(work, list, GGenericWork, link); } /****************************************************************************** * * -* Paramètres : group = gestionnaire des actions à mener. * -* * -* Description : Assure le traitement en différé. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void *g_work_group_process(GWorkGroup *group) -{ - GDelayedWork *work; /* Traitement à mener */ - GtkStatusStack *status; /* Zone d'info éventuelle */ - - while (1) - { - g_mutex_lock(&group->mutex); - - while (dl_list_empty(group->works) && !group->force_exit) - g_cond_wait(&group->cond, &group->mutex); - - if (group->force_exit) - { - g_mutex_unlock(&group->mutex); - break; - } - - work = group->works; - delayed_work_list_del(work, &group->works); - - g_mutex_unlock(&group->mutex); - -#ifdef INCLUDE_GTK_SUPPORT - status = get_global_status(); -#else - status = NULL; -#endif - g_delayed_work_process(work, status); - - g_object_unref(G_OBJECT(work)); - - /** - * Verrou ou pas verrou ? - * - * La documentation de la GLib indique que ce n'est pas nécessaire : - * - * ''' - * It is good practice to lock the same mutex as the waiting threads - * while calling this function, though not required. - * ''' - * - * Ce conseil se trouve verbatim à l'adresse : - * - * https://developer.gnome.org/glib/stable/glib-Threads.html#g-cond-broadcast - * - * Dans la pratique, il peut arriver que l'attente de la fonction - * g_work_group_wait_for_completion() ne soit jamais interrompue. - * - * La documentation POSIX est un peu plus orientée : - * - * ''' - * The pthread_cond_broadcast() functions may be called by a thread - * whether or not it currently owns the mutex that threads calling - * pthread_cond_wait() have associated with the condition variable - * during their waits; however, if predictable scheduling behavior is - * required, then that mutex shall be locked by the thread calling - * pthread_cond_broadcast(). - * ''' - * - * Ce passage complet est consultable à l'adresse : - * - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html - * - * La page de manuel pthread_cond_broadcast(3) est quant à elle plus - * directrice : aucun complément d'information sur le sujet n'est fourni - * et les exemples associés utilisent implicement un verrou pendant - * sont appel. - */ - - g_mutex_lock(&group->mutex); - - if (g_atomic_int_dec_and_test(&group->pending)) - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - - } - - return NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à consulter. * -* * -* Description : Détermine si le groupe est vide de toute programmation. * +* Paramètres : work = travail à traiter. * +* list = ensemble de travaux à considérer. [OUT] * * * -* Retour : Etat du groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_work_group_is_empty(GWorkGroup *group) -{ - bool result; /* Etat à retourner */ - - /** - * Pour que le résultat soit exploitable, il ne doit pas varier - * en dehors de la zone couverte par le verrou du groupe avant - * son utilisation par l'appelant. - * - * Il doit donc logiquement y avoir un autre verrou en amont et, - * comme à priori on ne devrait pas bloquer les groupes principaux - * pour un traitement particulier, cette procédure ne devrait concerner - * que des groupes dynamiques. - */ - - g_mutex_lock(&group->mutex); - - result = dl_list_empty(group->works); - - g_mutex_unlock(&group->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : group = groupe dont les conclusions sont attendues. * -* queue = queue d'appartenance pour les appels externes. * -* * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * +* Description : Supprime un travail d'une liste de tâches à effectuer. * * * * Retour : - * * * @@ -726,530 +175,18 @@ static bool g_work_group_is_empty(GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue) +void g_generic_work_remove_from_list(GGenericWork *work, GGenericWork **list) { - wait_for_incoming_works_cb callback; /* Procédure complémentaire */ - - bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data) - { - return false; - } - - callback = group->callback != NULL ? group->callback : no_extra_check; - - g_mutex_lock(&group->mutex); - - /** - * On attend que : - * - la liste des tâches programmées soit vide. - * - il n'existe plus de tâche en cours. - * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. - */ - - while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data)) - && !group->force_exit) - { - g_cond_wait(&group->wait_cond, &group->mutex); - } - - g_mutex_unlock(&group->mutex); + dl_list_del(work, list, GGenericWork, link); } /****************************************************************************** * * -* Paramètres : group = groupe dont les paramètres sont à modifier. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data) -{ - group->callback = callback; - group->data = data; - -} - - -/****************************************************************************** +* Paramètres : work = travail à effectuer. * * * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* * -* Description : Force un réveil d'une attente en cours pour la confirmer. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_wake_up_waiters(GWorkGroup *group) -{ - /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). - */ - - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* TRAITEMENT DE TACHES DIFFEREES */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour le gestionnaire des travaux différés. */ -G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_class_init(GWorkQueueClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_work_queue_dispose; - object->finalize = (GObjectFinalizeFunc)g_work_queue_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance à initialiser. * -* * -* Description : Initialise une instance de gestionnaire de travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_init(GWorkQueue *queue) -{ - queue->generator = 0; - - queue->groups = NULL; - queue->groups_count = 0; - g_mutex_init(&queue->mutex); - g_cond_init(&queue->wait_all); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_dispose(GWorkQueue *queue) -{ - size_t i; /* Boucle de parcours */ - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - g_clear_object(&queue->groups[i]); - - g_mutex_unlock(&queue->mutex); - - g_mutex_clear(&queue->mutex); - g_cond_clear(&queue->wait_all); - - G_OBJECT_CLASS(g_work_queue_parent_class)->dispose(G_OBJECT(queue)); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_finalize(GWorkQueue *queue) -{ - if (queue->groups != NULL) - free(queue->groups); - - G_OBJECT_CLASS(g_work_queue_parent_class)->finalize(G_OBJECT(queue)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Créé un nouveau gestionnaire de tâches parallèles. * -* * -* Retour : Gestionnaire de traitements mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GWorkQueue *g_work_queue_new(void) -{ - GWorkQueue *result; /* Instance à retourner */ - - result = g_object_new(G_TYPE_WORK_QUEUE, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Donne l'assurance de l'existence d'un groupe de travail. * -* * -* Retour : true si un nouveau groupe a été constitué, false sinon. * -* * -* Remarques : Le verrou d'accès doit être posé par l'appelant. * -* * -******************************************************************************/ - -static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count) -{ - bool found; /* Bilan des recherches */ - size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe à consulter */ - - assert(!g_mutex_trylock(&queue->mutex)); - - found = false; - - for (i = 0; i < queue->groups_count && !found; i++) - { - group = queue->groups[i]; - found = (g_work_group_get_id(group) == id); - } - - if (!found) - { - queue->groups_count++; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); - - group = g_work_group_new(id, count); - queue->groups[queue->groups_count - 1] = group; - - } - - return !found; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* * -* Description : Constitue un nouveau groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, NULL); - } - while (!created); - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Constitue un nouveau petit groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, &count); - } - while (!created); - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Dissout un groupe de travail existant. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) -{ - size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe de travail manipulé */ -#ifndef NDEBUG - bool found; /* Repérage du groupe visé */ -#endif - -#ifndef NDEBUG - found = false; -#endif - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - { - group = queue->groups[i]; - - if (g_work_group_get_id(group) == id) - { - g_object_unref(G_OBJECT(group)); - - memmove(&queue->groups[i], &queue->groups[i + 1], - (queue->groups_count - i - 1) * sizeof(GWorkGroup *)); - - queue->groups_count--; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); - -#ifndef NDEBUG - found = true; -#endif - - break; - - } - - } - - assert(found); - - g_cond_broadcast(&queue->wait_all); - - g_mutex_unlock(&queue->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire des actions à mener. * -* work = nouvelle tâche à programmer, puis effectuer. * -* id = identifiant du groupe de travail d'affectation. * -* * -* Description : Place une nouvelle tâche en attente. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - assert(group != NULL); - - g_work_group_schedule(group, work); - - g_object_unref(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Fournit le groupe de travail correspondant à un identifiant. * -* * -* Retour : Eventuel groupe existant trouvé ou NULL si aucun. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *result; /* Trouvaille à retourner */ - size_t i; /* Boucle de parcours */ - - result = NULL; - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - if (g_work_group_get_id(queue->groups[i]) == id) - { - result = queue->groups[i]; - g_object_ref(G_OBJECT(result)); - break; - } - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Détermine si un groupe est vide de toute programmation. * -* * -* Retour : Etat du groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id) -{ - bool result; /* Etat à retourner */ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - result = g_work_group_is_empty(group); - g_object_unref(G_OBJECT(group)); - } - - else - result = true; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - g_work_group_wait_for_completion(group, queue); - g_object_unref(G_OBJECT(group)); - } - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* gb_ids = identifiants de groupes globaux. * -* gb_count = nombre de ces groupes globaux. * -* * -* Description : Attend que toutes les tâches de tout groupe soient traitées. * +* Description : Mène l'opération programmée. * * * * Retour : - * * * @@ -1257,80 +194,27 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) * * ******************************************************************************/ -void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t *gb_ids, size_t gb_count) +void g_generic_work_process(GGenericWork *work) { - size_t i; /* Boucle de parcours */ - - g_mutex_lock(&queue->mutex); - - wait_again: - - /** - * Attente d'éventuels groupes isolés. - */ - - while (queue->groups_count > gb_count) - g_cond_wait(&queue->wait_all, &queue->mutex); - - g_mutex_unlock(&queue->mutex); - - /** - * Attente des groupes principaux. - */ - - for (i = 0; i < gb_count; i++) - g_work_queue_wait_for_completion(queue, gb_ids[i]); + G_GENERIC_WORK_GET_CLASS(work)->run(work); - /** - * Si le groupe par défaut a généré de nouveaux groupes, on recommence ! - */ - - g_mutex_lock(&queue->mutex); - - if (queue->groups_count > gb_count) - goto wait_again; - - g_mutex_unlock(&queue->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + g_mutex_lock(&work->mutex); -void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data) -{ - GWorkGroup *group; /* Groupe de travail à traiter */ + work->completed = true; - group = g_work_queue_find_group_for_id(queue, id); + g_cond_signal(&work->cond); + g_mutex_unlock(&work->mutex); - if (group != NULL) - { - g_work_group_set_extra_wait_callback(group, callback, data); - g_object_unref(G_OBJECT(group)); - } + g_signal_emit_by_name(work, "work-completed"); } /****************************************************************************** * * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * +* Paramètres : work = travail à surveiller. * * * -* Description : Force un réveil d'une attente en cours pour la confirmer. * +* Description : Attend la fin de l'exécution d'une tâche donnée. * * * * Retour : - * * * @@ -1338,16 +222,13 @@ void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wai * * ******************************************************************************/ -void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id) +void g_generic_work_wait_for_completion(GGenericWork *work) { - GWorkGroup *group; /* Groupe de travail à traiter */ + g_mutex_lock(&work->mutex); - group = g_work_queue_find_group_for_id(queue, id); + while (!work->completed) + g_cond_wait(&work->cond, &work->mutex); - if (group != NULL) - { - g_work_group_wake_up_waiters(group); - g_object_unref(G_OBJECT(group)); - } + g_mutex_unlock(&work->mutex); } diff --git a/src/glibext/work.h b/src/glibext/work.h index 89eed12..1084942 100644 --- a/src/glibext/work.h +++ b/src/glibext/work.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.h - prototypes pour la gestion des travaux différés + * work.h - prototypes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,107 +21,31 @@ */ -#ifndef _GLIBEXT_DELAYED_H -#define _GLIBEXT_DELAYED_H +#ifndef _GLIBEXT_WORK_H +#define _GLIBEXT_WORK_H -#include <glib-object.h> -#include <stdbool.h> -#include <stdint.h> +#include "helpers.h" -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ +#define G_TYPE_GENERIC_WORK (g_generic_work_get_type()) +DECLARE_GTYPE(GGenericWork, g_generic_work, G, GENERIC_WORK); -#define G_TYPE_DELAYED_WORK g_delayed_work_get_type() -#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork)) -#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type())) -#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) -#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK)) -#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) +/* Intègre un travail dans une liste de tâches à effectuer. */ +void g_generic_work_add_to_list(GGenericWork *, GGenericWork **); -/* Travail différé (instance) */ -typedef struct _GDelayedWork GDelayedWork; +/* Supprime un travail d'une liste de tâches à effectuer. */ +void g_generic_work_remove_from_list(GGenericWork *, GGenericWork **); -/* Travail différé (classe) */ -typedef struct _GDelayedWorkClass GDelayedWorkClass; - - -/* Indique le type défini pour les travaux différés. */ -GType g_delayed_work_get_type(void); +/* Mène l'opération programmée. */ +void g_generic_work_process(GGenericWork *); /* Attend la fin de l'exécution d'une tâche donnée. */ -void g_delayed_work_wait_for_completion(GDelayedWork *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -#define G_TYPE_WORK_QUEUE g_work_queue_get_type() -#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue)) -#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type())) -#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass)) -#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE)) -#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass)) - - -/* Gestionnaire des travaux différés (instance) */ -typedef struct _GWorkQueue GWorkQueue; - -/* Gestionnaire des travaux différés (classe) */ -typedef struct _GWorkQueueClass GWorkQueueClass; - - -/** - * Identifiant unique pour groupe de travail. - * - * Le nombre de bits est forcé à 64 bits car glib-genmarshal ne reconnait - * pas explicitement le type 'unsigned long long'. - */ -typedef uint64_t wgroup_id_t; - - -/* Indique le type défini pour le gestionnaire des travaux différés. */ -GType g_work_queue_get_type(void); - -/* Créé un nouveau gestionnaire de tâches parallèles. */ -GWorkQueue *g_work_queue_new(void); - -/* Constitue un nouveau groupe de travail. */ -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *); - -/* Constitue un nouveau petit groupe de travail. */ -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint); - -/* Dissout un groupe de travail existant. */ -void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t); - -/* Place une nouvelle tâche en attente. */ -void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t); - -/* Détermine si un groupe est vide de toute programmation. */ -bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t); - -/* Attend que toutes les tâches d'un groupe soient traitées. */ -void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t); - -/* Attend que toutes les tâches de tout groupe soient traitées. */ -void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t); - - -/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */ -typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *); - - -/* Modifie les conditions d'attente des fins d'exécutions. */ -void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *); - -/* Force un réveil d'une attente en cours pour la confirmer. */ -void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t); +void g_generic_work_wait_for_completion(GGenericWork *); -#endif /* _GLIBEXT_DELAYED_H */ +#endif /* _GLIBEXT_WORK_H */ diff --git a/src/glibext/workgroup-int.h b/src/glibext/workgroup-int.h index 4f84e86..7224cf9 100644 --- a/src/glibext/workgroup-int.h +++ b/src/glibext/workgroup-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed-int.h - définitions internes pour la gestion des travaux différés + * workgroup-int.h - définitions internes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,55 +21,40 @@ */ -#ifndef _GLIBEXT_DELAYED_INT_H -#define _GLIBEXT_DELAYED_INT_H +#ifndef _GLIBEXT_WORKGROUP_INT_H +#define _GLIBEXT_WORKGROUP_INT_H -#include "delayed.h" +#include "workgroup.h" -#include "notifier.h" -#include "../common/dllist.h" - - -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - - -/* Traite un travail programmé. */ -typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *); - - -/* Travail différé (instance) */ -struct _GDelayedWork +/* File de traitement pour un type donné (instance) */ +typedef struct _GWorkGroup { GObject parent; /* A laisser en premier */ - DL_LIST_ITEM(link); /* Lien vers les maillons */ + wgroup_id_t id; /* Identifiant de travaux menés*/ - bool completed; /* Fin de la tâche ? */ - GMutex mutex; /* Accès à la variable */ - GCond cond; /* Attente de changement */ + GGenericWork *works; /* Tâches à mener à bien */ + GMutex mutex; /* Verrou pour l'accès */ + GCond cond; /* Réveil pour un traitement */ + GCond wait_cond; /* Réveil d'attente de fin */ + gint pending; /* Tâches en cours d'exécution */ -}; + GThread **threads; /* Procédure de traitement */ + guint threads_count; /* Nombre de procédures */ + bool force_exit; /* Procédure d'arrêt */ -/* Travail différé (classe) */ -struct _GDelayedWorkClass +} GWorkGroup; + +/* File de traitement pour un type donné (classe) */ +typedef struct _GWorkGroupClass { GObjectClass parent; /* A laisser en premier */ - run_task_fc run; /* Traitement externalisé */ - - /* Signaux */ - - void (* work_completed) (GDelayedWork *); - -}; - - -#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link) -#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link) +} GWorkGroupClass; -#endif /* _GLIBEXT_DELAYED_INT_H */ +#endif /* _GLIBEXT_WORKGROUP_INT_H */ diff --git a/src/glibext/workgroup.c b/src/glibext/workgroup.c index 6b5ac35..c603d54 100644 --- a/src/glibext/workgroup.c +++ b/src/glibext/workgroup.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.c - gestion des travaux différés + * workgroup.c - gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,88 +21,21 @@ */ -#include "delayed.h" +#include "workgroup.h" #include <assert.h> #include <inttypes.h> #include <malloc.h> #include <stdio.h> -#include <string.h> -#include "delayed-int.h" +#include "workgroup-int.h" +#include "../common/dllist.h" #include "../core/nproc.h" -#ifdef INCLUDE_GTK_SUPPORT -# include "../gui/core/global.h" -#endif -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - - -/* Initialise la classe des travaux différés. */ -static void g_delayed_work_class_init(GDelayedWorkClass *); - -/* Initialise une instance de travail différé. */ -static void g_delayed_work_init(GDelayedWork *); - -/* Supprime toutes les références externes. */ -static void g_delayed_work_dispose(GDelayedWork *); - -/* Procède à la libération totale de la mémoire. */ -static void g_delayed_work_finalize(GDelayedWork *); - -/* Mène l'opération programmée. */ -static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *); - - - -/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */ - - -#define G_TYPE_WORK_GROUP g_work_group_get_type() -#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup)) -#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type())) -#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass)) -#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP)) -#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass)) - - -/* File de traitement pour un type donné (instance) */ -typedef struct _GWorkGroup -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t id; /* Identifiant de travaux menés*/ - - GDelayedWork *works; /* Tâches à mener à bien */ - GMutex mutex; /* Verrou pour l'accès */ - GCond cond; /* Réveil pour un traitement */ - GCond wait_cond; /* Réveil d'attente de fin */ - gint pending; /* Tâches en cours d'exécution */ - - GThread **threads; /* Procédure de traitement */ - guint threads_count; /* Nombre de procédures */ - bool force_exit; /* Procédure d'arrêt */ - - wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */ - void *data; /* Données à associer */ - -} GWorkGroup; - -/* File de traitement pour un type donné (classe) */ -typedef struct _GWorkGroupClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GWorkGroupClass; - - -/* Indique le type défini pour les groupes de travail. */ -static GType g_work_group_get_type(void); - /* Initialise la classe des groupes de travail. */ static void g_work_group_class_init(GWorkGroupClass *); @@ -115,237 +48,9 @@ static void g_work_group_dispose(GWorkGroup *); /* Procède à la libération totale de la mémoire. */ static void g_work_group_finalize(GWorkGroup *); -/* Crée un nouveau thread dédié à un type de travaux donné. */ -static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *); - -/* Fournit l'identifiant associé à un groupe de travail. */ -static wgroup_id_t g_work_group_get_id(const GWorkGroup *); - -/* Place une nouvelle tâche en attente dans une file dédiée. */ -static void g_work_group_schedule(GWorkGroup *, GDelayedWork *); - /* Assure le traitement en différé. */ static void *g_work_group_process(GWorkGroup *); -/* Détermine si le groupe est vide de toute programmation. */ -static bool g_work_group_is_empty(GWorkGroup *); - -/* Attend que toutes les tâches d'un groupe soient traitées. */ -static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *); - -/* Modifie les conditions d'attente des fins d'exécutions. */ -static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *); - -/* Force un réveil d'une attente en cours pour la confirmer. */ -static void g_work_group_wake_up_waiters(GWorkGroup *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -/* Gestionnaire des travaux différés (instance) */ -struct _GWorkQueue -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t generator; /* Générateur d'identifiants */ - - GWorkGroup **groups; /* Files de traitement */ - size_t groups_count; /* Nombre de files internes */ - GMutex mutex; /* Verrou pour l'accès */ - GCond wait_all; /* Réveil d'attente globale */ - -}; - -/* Gestionnaire des travaux différés (classe) */ -struct _GWorkQueueClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; - - -/* Initialise la classe des travaux différés. */ -static void g_work_queue_class_init(GWorkQueueClass *); - -/* Initialise une instance de gestionnaire de travaux différés. */ -static void g_work_queue_init(GWorkQueue *); - -/* Supprime toutes les références externes. */ -static void g_work_queue_dispose(GWorkQueue *); - -/* Procède à la libération totale de la mémoire. */ -static void g_work_queue_finalize(GWorkQueue *); - -/* Donne l'assurance de l'existence d'un groupe de travail. */ -static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *); - -/* Fournit le groupe de travail correspondant à un identifiant. */ -static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t); - - - -/* ---------------------------------------------------------------------------------- */ -/* TACHE DIFFEREE DANS LE TEMPS */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour les travaux différés. */ -G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_class_init(GDelayedWorkClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose; - object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize; - - g_signal_new("work-completed", - G_TYPE_DELAYED_WORK, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GDelayedWorkClass, work_completed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance à initialiser. * -* * -* Description : Initialise une instance de travail différé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_init(GDelayedWork *work) -{ - work->completed = false; - g_mutex_init(&work->mutex); - g_cond_init(&work->cond); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_dispose(GDelayedWork *work) -{ - g_mutex_clear(&work->mutex); - g_cond_clear(&work->cond); - - G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_finalize(GDelayedWork *work) -{ - G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à effectuer. * -* status = barre de statut à tenir informée. * -* * -* Description : Mène l'opération programmée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status) -{ - G_DELAYED_WORK_GET_CLASS(work)->run(work, status); - - g_mutex_lock(&work->mutex); - - work->completed = true; - - g_cond_signal(&work->cond); - g_mutex_unlock(&work->mutex); - - g_signal_emit_by_name(work, "work-completed"); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à surveiller. * -* * -* Description : Attend la fin de l'exécution d'une tâche donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_delayed_work_wait_for_completion(GDelayedWork *work) -{ - g_mutex_lock(&work->mutex); - - while (!work->completed) - g_cond_wait(&work->cond, &work->mutex); - - g_mutex_unlock(&work->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* THREADS DES TRAITEMENTS DEDIES */ -/* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour les groupes de travail. */ @@ -390,7 +95,7 @@ static void g_work_group_class_init(GWorkGroupClass *klass) static void g_work_group_init(GWorkGroup *group) { - group->works = NULL; + DL_LIST_HEAD_INIT(group->works); g_mutex_init(&group->mutex); g_cond_init(&group->cond); @@ -402,15 +107,12 @@ static void g_work_group_init(GWorkGroup *group) group->threads_count = 0; group->force_exit = false; - group->callback = NULL; - group->data = NULL; - } /****************************************************************************** * * -* Paramètres : queue = instance d'objet GLib à traiter. * +* Paramètres : group = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -423,7 +125,7 @@ static void g_work_group_init(GWorkGroup *group) static void g_work_group_dispose(GWorkGroup *group) { guint i; /* Boucle de parcours */ - GDelayedWork *work; /* Travail à oublier */ + GGenericWork *work; /* Travail à oublier */ group->force_exit = true; @@ -444,9 +146,9 @@ static void g_work_group_dispose(GWorkGroup *group) while (!dl_list_empty(group->works)) { work = group->works; - delayed_work_list_del(work, &group->works); + g_generic_work_remove_from_list(work, &group->works); - g_object_unref(G_OBJECT(work)); + unref_object(work); } @@ -484,7 +186,7 @@ static void g_work_group_finalize(GWorkGroup *group) /****************************************************************************** * * * Paramètres : id = identifiant accordé au nouveau groupe. * -* count = quantité de threads à allouer. * +* count = quantité de threads à allouer (0 pour un défaut). * * * * Description : Crée un nouveau thread dédié à un type de travaux donné. * * * @@ -494,33 +196,47 @@ static void g_work_group_finalize(GWorkGroup *group) * * ******************************************************************************/ -static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count) +GWorkGroup *g_work_group_new(wgroup_id_t id, guint count) { - GWorkGroup *result; /* Traiteur à retourner */ + GWorkGroup *result; /* Traiteur à retourner */ guint i; /* Boucle de parcours */ - char name[16]; /* Désignation humaine */ + int ret; /* Bilan d'un appel */ + char *name; /* Désignation humaine */ result = g_object_new(G_TYPE_WORK_GROUP, NULL); result->id = id; - result->threads_count = get_max_online_threads(); + if (count == 0) + count = get_max_online_threads(); - if (count != NULL && *count < result->threads_count) - result->threads_count = *count; + result->threads_count = count; - result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *)); + result->threads = calloc(result->threads_count, sizeof(GThread *)); for (i = 0; i < result->threads_count; i++) { - snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i); + /** + * La documentation précise : + * + * Some systems restrict the length of name to 16 bytes. + * + * On laisse ces systèmes tronquer. + */ + + ret = asprintf(&name, "wgrp_%" PRIu64 "-%u", id, i); + if (ret == -1) goto naming_error; result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result); + + free(name); + if (!result->threads[i]) goto start_error; } + naming_error: start_error: result->threads_count = i; @@ -544,7 +260,7 @@ static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count) * * ******************************************************************************/ -static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) +wgroup_id_t g_work_group_get_id(const GWorkGroup *group) { return group->id; @@ -564,13 +280,14 @@ static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) +void g_work_group_schedule(GWorkGroup *group, GGenericWork *work) { g_mutex_lock(&group->mutex); g_atomic_int_inc(&group->pending); - delayed_work_list_add_tail(work, &group->works); + ref_object(work); + g_generic_work_add_to_list(work, &group->works); g_cond_signal(&group->cond); @@ -593,8 +310,7 @@ static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) static void *g_work_group_process(GWorkGroup *group) { - GDelayedWork *work; /* Traitement à mener */ - GtkStatusStack *status; /* Zone d'info éventuelle */ + GGenericWork *work; /* Traitement à mener */ while (1) { @@ -610,18 +326,13 @@ static void *g_work_group_process(GWorkGroup *group) } work = group->works; - delayed_work_list_del(work, &group->works); + g_generic_work_remove_from_list(work, &group->works); g_mutex_unlock(&group->mutex); -#ifdef INCLUDE_GTK_SUPPORT - status = get_global_status(); -#else - status = NULL; -#endif - g_delayed_work_process(work, status); + g_generic_work_process(work); - g_object_unref(G_OBJECT(work)); + unref_object(work); /** * Verrou ou pas verrou ? @@ -687,7 +398,7 @@ static void *g_work_group_process(GWorkGroup *group) * * ******************************************************************************/ -static bool g_work_group_is_empty(GWorkGroup *group) +bool g_work_group_is_empty(GWorkGroup *group) { bool result; /* Etat à retourner */ @@ -716,7 +427,6 @@ static bool g_work_group_is_empty(GWorkGroup *group) /****************************************************************************** * * * Paramètres : group = groupe dont les conclusions sont attendues. * -* queue = queue d'appartenance pour les appels externes. * * * * Description : Attend que toutes les tâches d'un groupe soient traitées. * * * @@ -726,17 +436,8 @@ static bool g_work_group_is_empty(GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue) +void g_work_group_wait_for_completion(GWorkGroup *group) { - wait_for_incoming_works_cb callback; /* Procédure complémentaire */ - - bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data) - { - return false; - } - - callback = group->callback != NULL ? group->callback : no_extra_check; - g_mutex_lock(&group->mutex); /** @@ -746,7 +447,7 @@ static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queu * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. */ - while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data)) + while ((g_atomic_int_get(&group->pending) > 0) && !group->force_exit) { g_cond_wait(&group->wait_cond, &group->mutex); @@ -759,456 +460,43 @@ static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queu /****************************************************************************** * * -* Paramètres : group = groupe dont les paramètres sont à modifier. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * +* Paramètres : group = groupe dont les conclusions sont attendues. * +* rel = durée relative à patienter au max. en microsecondes. * * * -* Description : Modifie les conditions d'attente des fins d'exécutions. * +* Description : Attend que toutes les tâches d'un groupe soient traitées. * * * -* Retour : - * +* Retour : Bilan de l'attente : false en cas d'expiration, true sinon. * * * -* Remarques : - * +* Remarques : Cette fonction est originellement dédiée à un usage Python. * * * ******************************************************************************/ -static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data) +bool g_work_group_wait_timed_for_completion(GWorkGroup *group, gint64 rel) { - group->callback = callback; - group->data = data; + bool result; /* Bilan d'attente à renvoyer */ + gint64 end_time; /* Borne de fin de l'attente */ -} + result = true; + g_mutex_lock(&group->mutex); -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* * -* Description : Force un réveil d'une attente en cours pour la confirmer. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + end_time = g_get_monotonic_time() + rel; -static void g_work_group_wake_up_waiters(GWorkGroup *group) -{ /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). + * On attend que : + * - la liste des tâches programmées soit vide. + * - il n'existe plus de tâche en cours. + * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. */ - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* TRAITEMENT DE TACHES DIFFEREES */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour le gestionnaire des travaux différés. */ -G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_class_init(GWorkQueueClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_work_queue_dispose; - object->finalize = (GObjectFinalizeFunc)g_work_queue_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance à initialiser. * -* * -* Description : Initialise une instance de gestionnaire de travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_init(GWorkQueue *queue) -{ - queue->generator = 0; - - queue->groups = NULL; - queue->groups_count = 0; - g_mutex_init(&queue->mutex); - g_cond_init(&queue->wait_all); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_dispose(GWorkQueue *queue) -{ - size_t i; /* Boucle de parcours */ - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - g_clear_object(&queue->groups[i]); - - g_mutex_unlock(&queue->mutex); - - g_mutex_clear(&queue->mutex); - g_cond_clear(&queue->wait_all); - - G_OBJECT_CLASS(g_work_queue_parent_class)->dispose(G_OBJECT(queue)); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_finalize(GWorkQueue *queue) -{ - if (queue->groups != NULL) - free(queue->groups); - - G_OBJECT_CLASS(g_work_queue_parent_class)->finalize(G_OBJECT(queue)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Créé un nouveau gestionnaire de tâches parallèles. * -* * -* Retour : Gestionnaire de traitements mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GWorkQueue *g_work_queue_new(void) -{ - GWorkQueue *result; /* Instance à retourner */ - - result = g_object_new(G_TYPE_WORK_QUEUE, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Donne l'assurance de l'existence d'un groupe de travail. * -* * -* Retour : true si un nouveau groupe a été constitué, false sinon. * -* * -* Remarques : Le verrou d'accès doit être posé par l'appelant. * -* * -******************************************************************************/ - -static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count) -{ - bool found; /* Bilan des recherches */ - size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe à consulter */ - - assert(!g_mutex_trylock(&queue->mutex)); - - found = false; - - for (i = 0; i < queue->groups_count && !found; i++) - { - group = queue->groups[i]; - found = (g_work_group_get_id(group) == id); - } - - if (!found) - { - queue->groups_count++; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); - - group = g_work_group_new(id, count); - queue->groups[queue->groups_count - 1] = group; - - } - - return !found; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* * -* Description : Constitue un nouveau groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, NULL); - } - while (!created); - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Constitue un nouveau petit groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, &count); - } - while (!created); - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Dissout un groupe de travail existant. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) -{ - size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe de travail manipulé */ -#ifndef NDEBUG - bool found; /* Repérage du groupe visé */ -#endif - -#ifndef NDEBUG - found = false; -#endif - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - { - group = queue->groups[i]; - - if (g_work_group_get_id(group) == id) - { - g_object_unref(G_OBJECT(group)); - - memmove(&queue->groups[i], &queue->groups[i + 1], - (queue->groups_count - i - 1) * sizeof(GWorkGroup *)); - - queue->groups_count--; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); - -#ifndef NDEBUG - found = true; -#endif - - break; - - } - - } - - assert(found); - - g_cond_broadcast(&queue->wait_all); - - g_mutex_unlock(&queue->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire des actions à mener. * -* work = nouvelle tâche à programmer, puis effectuer. * -* id = identifiant du groupe de travail d'affectation. * -* * -* Description : Place une nouvelle tâche en attente. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - assert(group != NULL); - - g_work_group_schedule(group, work); - - g_object_unref(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Fournit le groupe de travail correspondant à un identifiant. * -* * -* Retour : Eventuel groupe existant trouvé ou NULL si aucun. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *result; /* Trouvaille à retourner */ - size_t i; /* Boucle de parcours */ - - result = NULL; - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - if (g_work_group_get_id(queue->groups[i]) == id) - { - result = queue->groups[i]; - g_object_ref(G_OBJECT(result)); - break; - } - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Détermine si un groupe est vide de toute programmation. * -* * -* Retour : Etat du groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id) -{ - bool result; /* Etat à retourner */ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) + while ((g_atomic_int_get(&group->pending) > 0) + && !group->force_exit) { - result = g_work_group_is_empty(group); - g_object_unref(G_OBJECT(group)); + result = g_cond_wait_until(&group->wait_cond, &group->mutex, end_time); + if (!result) break; } - else - result = true; + g_mutex_unlock(&group->mutex); return result; @@ -1217,39 +505,9 @@ bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id) /****************************************************************************** * * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - g_work_group_wait_for_completion(group, queue); - g_object_unref(G_OBJECT(group)); - } - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* gb_ids = identifiants de groupes globaux. * -* gb_count = nombre de ces groupes globaux. * +* Paramètres : group = groupes de travail à manipuler. * * * -* Description : Attend que toutes les tâches de tout groupe soient traitées. * +* Description : Force un réveil d'une attente en cours pour la confirmer. * * * * Retour : - * * * @@ -1257,97 +515,17 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) * * ******************************************************************************/ -void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t *gb_ids, size_t gb_count) +void g_work_group_wake_up_waiters(GWorkGroup *group) { - size_t i; /* Boucle de parcours */ - - g_mutex_lock(&queue->mutex); - - wait_again: - - /** - * Attente d'éventuels groupes isolés. - */ - - while (queue->groups_count > gb_count) - g_cond_wait(&queue->wait_all, &queue->mutex); - - g_mutex_unlock(&queue->mutex); - /** - * Attente des groupes principaux. - */ - - for (i = 0; i < gb_count; i++) - g_work_queue_wait_for_completion(queue, gb_ids[i]); - - /** - * Si le groupe par défaut a généré de nouveaux groupes, on recommence ! + * Concernant la pose du verrou, se référer aux commentaires de la + * fonction g_work_group_process(). */ - g_mutex_lock(&queue->mutex); - - if (queue->groups_count > gb_count) - goto wait_again; - - g_mutex_unlock(&queue->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data) -{ - GWorkGroup *group; /* Groupe de travail à traiter */ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - g_work_group_set_extra_wait_callback(group, callback, data); - g_object_unref(G_OBJECT(group)); - } - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* * -* Description : Force un réveil d'une attente en cours pour la confirmer. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à traiter */ + g_mutex_lock(&group->mutex); - group = g_work_queue_find_group_for_id(queue, id); + g_cond_broadcast(&group->wait_cond); - if (group != NULL) - { - g_work_group_wake_up_waiters(group); - g_object_unref(G_OBJECT(group)); - } + g_mutex_unlock(&group->mutex); } diff --git a/src/glibext/workgroup.h b/src/glibext/workgroup.h index 89eed12..f40155e 100644 --- a/src/glibext/workgroup.h +++ b/src/glibext/workgroup.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.h - prototypes pour la gestion des travaux différés + * workgroup.h - prototypes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,58 +21,22 @@ */ -#ifndef _GLIBEXT_DELAYED_H -#define _GLIBEXT_DELAYED_H +#ifndef _GLIBEXT_WORKGROUP_H +#define _GLIBEXT_WORKGROUP_H -#include <glib-object.h> #include <stdbool.h> #include <stdint.h> +#include "helpers.h" +#include "work.h" -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ -#define G_TYPE_DELAYED_WORK g_delayed_work_get_type() -#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork)) -#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type())) -#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) -#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK)) -#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) +#define G_TYPE_WORK_GROUP (g_work_group_get_type()) - -/* Travail différé (instance) */ -typedef struct _GDelayedWork GDelayedWork; - -/* Travail différé (classe) */ -typedef struct _GDelayedWorkClass GDelayedWorkClass; - - -/* Indique le type défini pour les travaux différés. */ -GType g_delayed_work_get_type(void); - -/* Attend la fin de l'exécution d'une tâche donnée. */ -void g_delayed_work_wait_for_completion(GDelayedWork *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -#define G_TYPE_WORK_QUEUE g_work_queue_get_type() -#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue)) -#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type())) -#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass)) -#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE)) -#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass)) - - -/* Gestionnaire des travaux différés (instance) */ -typedef struct _GWorkQueue GWorkQueue; - -/* Gestionnaire des travaux différés (classe) */ -typedef struct _GWorkQueueClass GWorkQueueClass; +DECLARE_GTYPE(GWorkGroup, g_work_group, G, WORK_GROUP); /** @@ -83,45 +47,33 @@ typedef struct _GWorkQueueClass GWorkQueueClass; */ typedef uint64_t wgroup_id_t; +/** + * Marque de non initialisation. + */ +#define INVALID_GROUP_ID 0 -/* Indique le type défini pour le gestionnaire des travaux différés. */ -GType g_work_queue_get_type(void); - -/* Créé un nouveau gestionnaire de tâches parallèles. */ -GWorkQueue *g_work_queue_new(void); - -/* Constitue un nouveau groupe de travail. */ -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *); -/* Constitue un nouveau petit groupe de travail. */ -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint); +/* Crée un nouveau thread dédié à un type de travaux donné. */ +GWorkGroup *g_work_group_new(wgroup_id_t, guint); -/* Dissout un groupe de travail existant. */ -void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t); +/* Fournit l'identifiant associé à un groupe de travail. */ +wgroup_id_t g_work_group_get_id(const GWorkGroup *); -/* Place une nouvelle tâche en attente. */ -void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t); +/* Place une nouvelle tâche en attente dans une file dédiée. */ +void g_work_group_schedule(GWorkGroup *, GGenericWork *); -/* Détermine si un groupe est vide de toute programmation. */ -bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t); +/* Détermine si le groupe est vide de toute programmation. */ +bool g_work_group_is_empty(GWorkGroup *); /* Attend que toutes les tâches d'un groupe soient traitées. */ -void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t); +void g_work_group_wait_for_completion(GWorkGroup *); -/* Attend que toutes les tâches de tout groupe soient traitées. */ -void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t); - - -/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */ -typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *); - - -/* Modifie les conditions d'attente des fins d'exécutions. */ -void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *); +/* Attend que toutes les tâches d'un groupe soient traitées. */ +bool g_work_group_wait_timed_for_completion(GWorkGroup *, gint64); /* Force un réveil d'une attente en cours pour la confirmer. */ -void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t); +void g_work_group_wake_up_waiters(GWorkGroup *); -#endif /* _GLIBEXT_DELAYED_H */ +#endif /* _GLIBEXT_WORKGROUP_H */ diff --git a/src/glibext/workqueue-int.h b/src/glibext/workqueue-int.h index 4f84e86..40afa19 100644 --- a/src/glibext/workqueue-int.h +++ b/src/glibext/workqueue-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed-int.h - définitions internes pour la gestion des travaux différés + * workqueue-int.h - définitions internes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,55 +21,35 @@ */ -#ifndef _GLIBEXT_DELAYED_INT_H -#define _GLIBEXT_DELAYED_INT_H +#ifndef _GLIBEXT_WORKQUEUE_INT_H +#define _GLIBEXT_WORKQUEUE_INT_H -#include "delayed.h" +#include "workqueue.h" -#include "notifier.h" -#include "../common/dllist.h" - - -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - - -/* Traite un travail programmé. */ -typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *); - - -/* Travail différé (instance) */ -struct _GDelayedWork +/* Gestionnaire des travaux différés (instance) */ +struct _GWorkQueue { GObject parent; /* A laisser en premier */ - DL_LIST_ITEM(link); /* Lien vers les maillons */ + wgroup_id_t generator; /* Générateur d'identifiants */ - bool completed; /* Fin de la tâche ? */ - GMutex mutex; /* Accès à la variable */ - GCond cond; /* Attente de changement */ + GWorkGroup **groups; /* Files de traitement */ + size_t groups_count; /* Nombre de files internes */ + GMutex mutex; /* Verrou pour l'accès */ + GCond wait_all; /* Réveil d'attente globale */ }; -/* Travail différé (classe) */ -struct _GDelayedWorkClass +/* Gestionnaire des travaux différés (classe) */ +struct _GWorkQueueClass { GObjectClass parent; /* A laisser en premier */ - run_task_fc run; /* Traitement externalisé */ - - /* Signaux */ - - void (* work_completed) (GDelayedWork *); - }; -#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link) -#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link) - - -#endif /* _GLIBEXT_DELAYED_INT_H */ +#endif /* _GLIBEXT_WORKQUEUE_INT_H */ diff --git a/src/glibext/workqueue.c b/src/glibext/workqueue.c index 6b5ac35..fe7f1bb 100644 --- a/src/glibext/workqueue.c +++ b/src/glibext/workqueue.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.c - gestion des travaux différés + * workqueue.c - gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,149 +21,15 @@ */ -#include "delayed.h" +#include "workqueue.h" #include <assert.h> -#include <inttypes.h> #include <malloc.h> -#include <stdio.h> -#include <string.h> -#include "delayed-int.h" -#include "../core/nproc.h" -#ifdef INCLUDE_GTK_SUPPORT -# include "../gui/core/global.h" -#endif - - - -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - - -/* Initialise la classe des travaux différés. */ -static void g_delayed_work_class_init(GDelayedWorkClass *); - -/* Initialise une instance de travail différé. */ -static void g_delayed_work_init(GDelayedWork *); - -/* Supprime toutes les références externes. */ -static void g_delayed_work_dispose(GDelayedWork *); - -/* Procède à la libération totale de la mémoire. */ -static void g_delayed_work_finalize(GDelayedWork *); - -/* Mène l'opération programmée. */ -static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *); - - - -/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */ - - -#define G_TYPE_WORK_GROUP g_work_group_get_type() -#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup)) -#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type())) -#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass)) -#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP)) -#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass)) - - -/* File de traitement pour un type donné (instance) */ -typedef struct _GWorkGroup -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t id; /* Identifiant de travaux menés*/ - - GDelayedWork *works; /* Tâches à mener à bien */ - GMutex mutex; /* Verrou pour l'accès */ - GCond cond; /* Réveil pour un traitement */ - GCond wait_cond; /* Réveil d'attente de fin */ - gint pending; /* Tâches en cours d'exécution */ - - GThread **threads; /* Procédure de traitement */ - guint threads_count; /* Nombre de procédures */ - bool force_exit; /* Procédure d'arrêt */ - - wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */ - void *data; /* Données à associer */ - -} GWorkGroup; - -/* File de traitement pour un type donné (classe) */ -typedef struct _GWorkGroupClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GWorkGroupClass; - - -/* Indique le type défini pour les groupes de travail. */ -static GType g_work_group_get_type(void); - -/* Initialise la classe des groupes de travail. */ -static void g_work_group_class_init(GWorkGroupClass *); - -/* Initialise une instance de groupe de travail. */ -static void g_work_group_init(GWorkGroup *); +#include "workqueue-int.h" -/* Supprime toutes les références externes. */ -static void g_work_group_dispose(GWorkGroup *); - -/* Procède à la libération totale de la mémoire. */ -static void g_work_group_finalize(GWorkGroup *); - -/* Crée un nouveau thread dédié à un type de travaux donné. */ -static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *); - -/* Fournit l'identifiant associé à un groupe de travail. */ -static wgroup_id_t g_work_group_get_id(const GWorkGroup *); - -/* Place une nouvelle tâche en attente dans une file dédiée. */ -static void g_work_group_schedule(GWorkGroup *, GDelayedWork *); - -/* Assure le traitement en différé. */ -static void *g_work_group_process(GWorkGroup *); - -/* Détermine si le groupe est vide de toute programmation. */ -static bool g_work_group_is_empty(GWorkGroup *); - -/* Attend que toutes les tâches d'un groupe soient traitées. */ -static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *); - -/* Modifie les conditions d'attente des fins d'exécutions. */ -static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *); - -/* Force un réveil d'une attente en cours pour la confirmer. */ -static void g_work_group_wake_up_waiters(GWorkGroup *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -/* Gestionnaire des travaux différés (instance) */ -struct _GWorkQueue -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t generator; /* Générateur d'identifiants */ - - GWorkGroup **groups; /* Files de traitement */ - size_t groups_count; /* Nombre de files internes */ - GMutex mutex; /* Verrou pour l'accès */ - GCond wait_all; /* Réveil d'attente globale */ - -}; - -/* Gestionnaire des travaux différés (classe) */ -struct _GWorkQueueClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; /* Initialise la classe des travaux différés. */ @@ -179,641 +45,13 @@ static void g_work_queue_dispose(GWorkQueue *); static void g_work_queue_finalize(GWorkQueue *); /* Donne l'assurance de l'existence d'un groupe de travail. */ -static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *); +static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, guint); /* Fournit le groupe de travail correspondant à un identifiant. */ static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t); -/* ---------------------------------------------------------------------------------- */ -/* TACHE DIFFEREE DANS LE TEMPS */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour les travaux différés. */ -G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_class_init(GDelayedWorkClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose; - object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize; - - g_signal_new("work-completed", - G_TYPE_DELAYED_WORK, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GDelayedWorkClass, work_completed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance à initialiser. * -* * -* Description : Initialise une instance de travail différé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_init(GDelayedWork *work) -{ - work->completed = false; - g_mutex_init(&work->mutex); - g_cond_init(&work->cond); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_dispose(GDelayedWork *work) -{ - g_mutex_clear(&work->mutex); - g_cond_clear(&work->cond); - - G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_finalize(GDelayedWork *work) -{ - G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à effectuer. * -* status = barre de statut à tenir informée. * -* * -* Description : Mène l'opération programmée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status) -{ - G_DELAYED_WORK_GET_CLASS(work)->run(work, status); - - g_mutex_lock(&work->mutex); - - work->completed = true; - - g_cond_signal(&work->cond); - g_mutex_unlock(&work->mutex); - - g_signal_emit_by_name(work, "work-completed"); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à surveiller. * -* * -* Description : Attend la fin de l'exécution d'une tâche donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_delayed_work_wait_for_completion(GDelayedWork *work) -{ - g_mutex_lock(&work->mutex); - - while (!work->completed) - g_cond_wait(&work->cond, &work->mutex); - - g_mutex_unlock(&work->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* THREADS DES TRAITEMENTS DEDIES */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour les groupes de travail. */ -G_DEFINE_TYPE(GWorkGroup, g_work_group, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des groupes de travail. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_class_init(GWorkGroupClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_work_group_dispose; - object->finalize = (GObjectFinalizeFunc)g_work_group_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : group = instance à initialiser. * -* * -* Description : Initialise une instance de groupe de travail. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_init(GWorkGroup *group) -{ - group->works = NULL; - - g_mutex_init(&group->mutex); - g_cond_init(&group->cond); - g_cond_init(&group->wait_cond); - - g_atomic_int_set(&group->pending, 0); - - group->threads = NULL; - group->threads_count = 0; - group->force_exit = false; - - group->callback = NULL; - group->data = NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_dispose(GWorkGroup *group) -{ - guint i; /* Boucle de parcours */ - GDelayedWork *work; /* Travail à oublier */ - - group->force_exit = true; - - /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). - */ - - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->cond); - - g_mutex_unlock(&group->mutex); - - for (i = 0; i < group->threads_count; i++) - g_thread_join(group->threads[i]); - - while (!dl_list_empty(group->works)) - { - work = group->works; - delayed_work_list_del(work, &group->works); - - g_object_unref(G_OBJECT(work)); - - } - - g_mutex_clear(&group->mutex); - g_cond_clear(&group->cond); - g_cond_clear(&group->wait_cond); - - G_OBJECT_CLASS(g_work_group_parent_class)->dispose(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : group = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_finalize(GWorkGroup *group) -{ - if (group->threads != NULL) - free(group->threads); - - G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : id = identifiant accordé au nouveau groupe. * -* count = quantité de threads à allouer. * -* * -* Description : Crée un nouveau thread dédié à un type de travaux donné. * -* * -* Retour : Structure associée au thread mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count) -{ - GWorkGroup *result; /* Traiteur à retourner */ - guint i; /* Boucle de parcours */ - char name[16]; /* Désignation humaine */ - - result = g_object_new(G_TYPE_WORK_GROUP, NULL); - - result->id = id; - - result->threads_count = get_max_online_threads(); - - if (count != NULL && *count < result->threads_count) - result->threads_count = *count; - - result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *)); - - for (i = 0; i < result->threads_count; i++) - { - snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i); - - result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result); - if (!result->threads[i]) - goto start_error; - - } - - start_error: - - result->threads_count = i; - - assert(i > 0); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à mener. * -* * -* Description : Fournit l'identifiant associé à un groupe de travail. * -* * -* Retour : Identifiant unique attribué au groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) -{ - return group->id; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à mener. * -* work = nouvelle tâche à programmer, puis effectuer. * -* * -* Description : Place une nouvelle tâche en attente dans une file dédiée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) -{ - g_mutex_lock(&group->mutex); - - g_atomic_int_inc(&group->pending); - - delayed_work_list_add_tail(work, &group->works); - - g_cond_signal(&group->cond); - - g_mutex_unlock(&group->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à mener. * -* * -* Description : Assure le traitement en différé. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void *g_work_group_process(GWorkGroup *group) -{ - GDelayedWork *work; /* Traitement à mener */ - GtkStatusStack *status; /* Zone d'info éventuelle */ - - while (1) - { - g_mutex_lock(&group->mutex); - - while (dl_list_empty(group->works) && !group->force_exit) - g_cond_wait(&group->cond, &group->mutex); - - if (group->force_exit) - { - g_mutex_unlock(&group->mutex); - break; - } - - work = group->works; - delayed_work_list_del(work, &group->works); - - g_mutex_unlock(&group->mutex); - -#ifdef INCLUDE_GTK_SUPPORT - status = get_global_status(); -#else - status = NULL; -#endif - g_delayed_work_process(work, status); - - g_object_unref(G_OBJECT(work)); - - /** - * Verrou ou pas verrou ? - * - * La documentation de la GLib indique que ce n'est pas nécessaire : - * - * ''' - * It is good practice to lock the same mutex as the waiting threads - * while calling this function, though not required. - * ''' - * - * Ce conseil se trouve verbatim à l'adresse : - * - * https://developer.gnome.org/glib/stable/glib-Threads.html#g-cond-broadcast - * - * Dans la pratique, il peut arriver que l'attente de la fonction - * g_work_group_wait_for_completion() ne soit jamais interrompue. - * - * La documentation POSIX est un peu plus orientée : - * - * ''' - * The pthread_cond_broadcast() functions may be called by a thread - * whether or not it currently owns the mutex that threads calling - * pthread_cond_wait() have associated with the condition variable - * during their waits; however, if predictable scheduling behavior is - * required, then that mutex shall be locked by the thread calling - * pthread_cond_broadcast(). - * ''' - * - * Ce passage complet est consultable à l'adresse : - * - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html - * - * La page de manuel pthread_cond_broadcast(3) est quant à elle plus - * directrice : aucun complément d'information sur le sujet n'est fourni - * et les exemples associés utilisent implicement un verrou pendant - * sont appel. - */ - - g_mutex_lock(&group->mutex); - - if (g_atomic_int_dec_and_test(&group->pending)) - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - - } - - return NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à consulter. * -* * -* Description : Détermine si le groupe est vide de toute programmation. * -* * -* Retour : Etat du groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_work_group_is_empty(GWorkGroup *group) -{ - bool result; /* Etat à retourner */ - - /** - * Pour que le résultat soit exploitable, il ne doit pas varier - * en dehors de la zone couverte par le verrou du groupe avant - * son utilisation par l'appelant. - * - * Il doit donc logiquement y avoir un autre verrou en amont et, - * comme à priori on ne devrait pas bloquer les groupes principaux - * pour un traitement particulier, cette procédure ne devrait concerner - * que des groupes dynamiques. - */ - - g_mutex_lock(&group->mutex); - - result = dl_list_empty(group->works); - - g_mutex_unlock(&group->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : group = groupe dont les conclusions sont attendues. * -* queue = queue d'appartenance pour les appels externes. * -* * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue) -{ - wait_for_incoming_works_cb callback; /* Procédure complémentaire */ - - bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data) - { - return false; - } - - callback = group->callback != NULL ? group->callback : no_extra_check; - - g_mutex_lock(&group->mutex); - - /** - * On attend que : - * - la liste des tâches programmées soit vide. - * - il n'existe plus de tâche en cours. - * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. - */ - - while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data)) - && !group->force_exit) - { - g_cond_wait(&group->wait_cond, &group->mutex); - } - - g_mutex_unlock(&group->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : group = groupe dont les paramètres sont à modifier. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data) -{ - group->callback = callback; - group->data = data; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* * -* Description : Force un réveil d'une attente en cours pour la confirmer. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_wake_up_waiters(GWorkGroup *group) -{ - /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). - */ - - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* TRAITEMENT DE TACHES DIFFEREES */ -/* ---------------------------------------------------------------------------------- */ - - /* Indique le type défini pour le gestionnaire des travaux différés. */ G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT); @@ -946,7 +184,7 @@ GWorkQueue *g_work_queue_new(void) * * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * * id = identifiant d'un groupe de travail. * -* count = quantité de threads à allouer. * +* count = quantité de threads à allouer (0 pour un défaut). * * * * Description : Donne l'assurance de l'existence d'un groupe de travail. * * * @@ -956,7 +194,7 @@ GWorkQueue *g_work_queue_new(void) * * ******************************************************************************/ -static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count) +static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, guint count) { bool found; /* Bilan des recherches */ size_t i; /* Boucle de parcours */ @@ -975,8 +213,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, if (!found) { queue->groups_count++; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); + queue->groups = realloc(queue->groups, queue->groups_count * sizeof(GWorkGroup *)); group = g_work_group_new(id, count); queue->groups[queue->groups_count - 1] = group; @@ -991,6 +228,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, /****************************************************************************** * * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * +* count = quantité de threads à allouer (0 pour un défaut). * * * * Description : Constitue un nouveau groupe de travail. * * * @@ -1000,7 +238,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, * * ******************************************************************************/ -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) +wgroup_id_t g_work_queue_define_group(GWorkQueue *queue, guint count) { wgroup_id_t result; /* Valeur à retourner */ bool created; /* Bilan d'une tentative */ @@ -1009,42 +247,13 @@ wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) do { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, NULL); - } - while (!created); + result = ++queue->generator; - g_mutex_unlock(&queue->mutex); + if (result == INVALID_GROUP_ID) + continue; - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Constitue un nouveau petit groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ + created = g_work_queue_ensure_group_exists(queue, result, count); -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, &count); } while (!created); @@ -1068,7 +277,7 @@ wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count) * * ******************************************************************************/ -void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) +void g_work_queue_delete_group(GWorkQueue *queue, wgroup_id_t id) { size_t i; /* Boucle de parcours */ GWorkGroup *group; /* Groupe de travail manipulé */ @@ -1088,14 +297,13 @@ void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) if (g_work_group_get_id(group) == id) { - g_object_unref(G_OBJECT(group)); + unref_object(group); memmove(&queue->groups[i], &queue->groups[i + 1], (queue->groups_count - i - 1) * sizeof(GWorkGroup *)); queue->groups_count--; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); + queue->groups = realloc(queue->groups, queue->groups_count * sizeof(GWorkGroup *)); #ifndef NDEBUG found = true; @@ -1124,22 +332,31 @@ void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) * * * Description : Place une nouvelle tâche en attente. * * * -* Retour : - * +* Retour : Bilan, qui correspond à l'existence du groupe ciblé. * * * * Remarques : - * * * ******************************************************************************/ -void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id) +bool g_work_queue_schedule(GWorkQueue *queue, GGenericWork *work, wgroup_id_t id) { + bool result; /* Bilan à retourner */ GWorkGroup *group; /* Groupe de travail à attendre*/ group = g_work_queue_find_group_for_id(queue, id); assert(group != NULL); - g_work_group_schedule(group, work); + result = (group != NULL); - g_object_unref(G_OBJECT(group)); + if (result) + { + g_work_group_schedule(group, work); + + unref_object(group); + + } + + return result; } @@ -1170,7 +387,7 @@ static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t if (g_work_group_get_id(queue->groups[i]) == id) { result = queue->groups[i]; - g_object_ref(G_OBJECT(result)); + ref_object(result); break; } @@ -1204,7 +421,7 @@ bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id) if (group != NULL) { result = g_work_group_is_empty(group); - g_object_unref(G_OBJECT(group)); + unref_object(group); } else @@ -1236,8 +453,8 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) if (group != NULL) { - g_work_group_wait_for_completion(group, queue); - g_object_unref(G_OBJECT(group)); + g_work_group_wait_for_completion(group); + unref_object(group); } } @@ -1245,6 +462,40 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) /****************************************************************************** * * +* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * +* id = identifiant d'un groupe de travail. * +* rel = durée relative à patienter au max. en microsecondes. * +* * +* Description : Attend que toutes les tâches d'un groupe soient traitées. * +* * +* Retour : Bilan de l'attente : false en cas d'expiration, true sinon. * +* * +* Remarques : Cette fonction est originellement dédiée à un usage Python. * +* * +******************************************************************************/ + +bool g_work_queue_wait_timed_for_completion(GWorkQueue *queue, wgroup_id_t id, gint64 rel) +{ + bool result; /* Bilan d'attente à renvoyer */ + GWorkGroup *group; /* Groupe de travail à attendre*/ + + group = g_work_queue_find_group_for_id(queue, id); + + if (group != NULL) + { + result = g_work_group_wait_timed_for_completion(group, rel); + unref_object(group); + } + else + result = true; + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* * gb_ids = identifiants de groupes globaux. * * gb_count = nombre de ces groupes globaux. * @@ -1299,36 +550,6 @@ void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t * * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* * id = identifiant d'un groupe de travail. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data) -{ - GWorkGroup *group; /* Groupe de travail à traiter */ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - g_work_group_set_extra_wait_callback(group, callback, data); - g_object_unref(G_OBJECT(group)); - } - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * * * * Description : Force un réveil d'une attente en cours pour la confirmer. * * * @@ -1347,7 +568,7 @@ void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id) if (group != NULL) { g_work_group_wake_up_waiters(group); - g_object_unref(G_OBJECT(group)); + unref_object(group); } } diff --git a/src/glibext/workqueue.h b/src/glibext/workqueue.h index 89eed12..963d86a 100644 --- a/src/glibext/workqueue.h +++ b/src/glibext/workqueue.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.h - prototypes pour la gestion des travaux différés + * workqueue.h - prototypes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,86 +21,34 @@ */ -#ifndef _GLIBEXT_DELAYED_H -#define _GLIBEXT_DELAYED_H +#ifndef _GLIBEXT_WORKQUEUE_H +#define _GLIBEXT_WORKQUEUE_H -#include <glib-object.h> #include <stdbool.h> -#include <stdint.h> +#include "helpers.h" +#include "workgroup.h" -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ -#define G_TYPE_DELAYED_WORK g_delayed_work_get_type() -#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork)) -#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type())) -#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) -#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK)) -#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) +#define G_TYPE_WORK_QUEUE (g_work_queue_get_type()) +DECLARE_GTYPE(GWorkQueue, g_work_queue, G, WORK_QUEUE); -/* Travail différé (instance) */ -typedef struct _GDelayedWork GDelayedWork; - -/* Travail différé (classe) */ -typedef struct _GDelayedWorkClass GDelayedWorkClass; - - -/* Indique le type défini pour les travaux différés. */ -GType g_delayed_work_get_type(void); - -/* Attend la fin de l'exécution d'une tâche donnée. */ -void g_delayed_work_wait_for_completion(GDelayedWork *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -#define G_TYPE_WORK_QUEUE g_work_queue_get_type() -#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue)) -#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type())) -#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass)) -#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE)) -#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass)) - - -/* Gestionnaire des travaux différés (instance) */ -typedef struct _GWorkQueue GWorkQueue; - -/* Gestionnaire des travaux différés (classe) */ -typedef struct _GWorkQueueClass GWorkQueueClass; - - -/** - * Identifiant unique pour groupe de travail. - * - * Le nombre de bits est forcé à 64 bits car glib-genmarshal ne reconnait - * pas explicitement le type 'unsigned long long'. - */ -typedef uint64_t wgroup_id_t; - - -/* Indique le type défini pour le gestionnaire des travaux différés. */ -GType g_work_queue_get_type(void); /* Créé un nouveau gestionnaire de tâches parallèles. */ GWorkQueue *g_work_queue_new(void); /* Constitue un nouveau groupe de travail. */ -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *); - -/* Constitue un nouveau petit groupe de travail. */ -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint); +wgroup_id_t g_work_queue_define_group(GWorkQueue *, guint); /* Dissout un groupe de travail existant. */ -void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t); +void g_work_queue_delete_group(GWorkQueue *, wgroup_id_t); /* Place une nouvelle tâche en attente. */ -void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t); +bool g_work_queue_schedule(GWorkQueue *, GGenericWork *, wgroup_id_t); /* Détermine si un groupe est vide de toute programmation. */ bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t); @@ -108,20 +56,15 @@ bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t); /* Attend que toutes les tâches d'un groupe soient traitées. */ void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t); +/* Attend que toutes les tâches d'un groupe soient traitées. */ +bool g_work_queue_wait_timed_for_completion(GWorkQueue *, wgroup_id_t, gint64); + /* Attend que toutes les tâches de tout groupe soient traitées. */ void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t); - -/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */ -typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *); - - -/* Modifie les conditions d'attente des fins d'exécutions. */ -void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *); - /* Force un réveil d'une attente en cours pour la confirmer. */ void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t); -#endif /* _GLIBEXT_DELAYED_H */ +#endif /* _GLIBEXT_WORKQUEUE_H */ diff --git a/src/gtkext/Makefile.am b/src/gtkext/Makefile.am index c9445e6..c6908dd 100644 --- a/src/gtkext/Makefile.am +++ b/src/gtkext/Makefile.am @@ -13,15 +13,11 @@ libgtkext_la_SOURCES = \ gtkblockdisplay.h gtkblockdisplay.c \ gtkdockable-int.h \ gtkdockable.h gtkdockable.c \ - gtkdockstation.h gtkdockstation.c \ gtkgraphdisplay.h gtkgraphdisplay.c \ - gtkstatusstack.h gtkstatusstack.c \ hexdisplay.h hexdisplay.c \ named-int.h \ named.h named.c \ - resources.h resources.c \ support.h support.c \ - tiledgrid.h tiledgrid.c \ tmgt.h tmgt.c libgtkext_la_LIBADD = \ @@ -30,9 +26,19 @@ libgtkext_la_LIBADD = \ libgtkext_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) +IMG_PATH = ../../data/images + RES_FILES = \ + grid.ui \ + dockstation.ui \ hexview.css \ - hexview.ui + hexview.ui \ + launcher.ui \ + statusstack.ui \ + $(IMG_PATH)/nolock-symbolic.svg \ + $(IMG_PATH)/locked-symbolic.svg \ + $(IMG_PATH)/unlocked-symbolic.svg \ + tweak.ui libgtkext4_la_SOURCES = \ area-int.h \ @@ -41,19 +47,36 @@ libgtkext4_la_SOURCES = \ bufferview.h bufferview.c \ contentview-int.h \ contentview.h contentview.c \ + dockstation-int.h \ + dockstation.h dockstation.c \ + grid-int.h \ + grid.h grid.c \ + helpers.h \ hexview-int.h \ hexview.h hexview.c \ - resources.h resources.c + launcher-int.h \ + launcher.h launcher.c \ + panel-int.h \ + panel.h panel.c \ + resources.h resources.c \ + statusstack-int.h \ + statusstack.h statusstack.c \ + tweak-int.h \ + tweak.h tweak.c libgtkext4_la_CFLAGS = $(LIBGTK4_CFLAGS) +libgtkext4_la_LIBADD = \ + bindings/libgtkextbindings.la + devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) -dev_HEADERS = $(libgtkext_la_SOURCES:%c=) +dev_HEADERS = $(libgtkext4_la_SOURCES:%c=) #SUBDIRS = graph +SUBDIRS = bindings resources.c: gresource.xml $(RES_FILES) diff --git a/src/gtkext/area.c b/src/gtkext/area.c index 3c59a59..3c573b2 100644 --- a/src/gtkext/area.c +++ b/src/gtkext/area.c @@ -85,6 +85,8 @@ static void gtk_composing_area_class_init(GtkComposingAreaClass *class) widget = GTK_WIDGET_CLASS(class); + gtk_widget_class_set_css_name(widget, "comparea"); + widget->snapshot = gtk_composing_area_snapshot; } diff --git a/src/gtkext/bindings/Makefile.am b/src/gtkext/bindings/Makefile.am new file mode 100644 index 0000000..71e770d --- /dev/null +++ b/src/gtkext/bindings/Makefile.am @@ -0,0 +1,42 @@ + +EXTRA_DIST = \ + generated-enums.c.template \ + generated-enums.h.template + + +noinst_LTLIBRARIES = libgtkextbindings.la + + +nodist_libgtkextbindings_la_SOURCES = \ + grid-enums.h grid-enums.c + +libgtkextbindings_la_CFLAGS = $(LIBGTK4_CFLAGS) + + +BUILT_SOURCES = $(nodist_libgtkextbindings_la_SOURCES) +CLEANFILES = $(nodist_libgtkextbindings_la_SOURCES) + + +devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) + +dev_HEADERS = $(nodist_libgtkextbindings_la_SOURCES:%c=) + + +grid-enums.c: ../../gtkext/grid.h generated-enums.c.template + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --fhead "#include \"$(subst .c,.h,$@)\"\n" \ + --template=$(srcdir)/generated-enums.c.template \ + --output=$(srcdir)/$@ \ + $< + +grid-enums.h: ../../gtkext/grid.h generated-enums.h.template + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --fhead "#ifndef _GTKEXT_BINDINGS_GRID_ENUM_H\n" \ + --fhead "#define _GTKEXT_BINDINGS_GRID_ENUM_H\n" \ + --fhead "\n" \ + --fhead "#include \"$<\"\n" \ + --fhead "\n" \ + --ftail "#endif /* _GTKEXT_BINDINGS_GRID_ENUM_H */\n" \ + --template=$(srcdir)/generated-enums.h.template \ + --output=$(srcdir)/$@ \ + $< diff --git a/src/gtkext/bindings/generated-enums.c.template b/src/gtkext/bindings/generated-enums.c.template new file mode 100644 index 0000000..8f54149 --- /dev/null +++ b/src/gtkext/bindings/generated-enums.c.template @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski <bartosz.golaszewski@linaro.org> + +/*** BEGIN file-header ***/ + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* Enumerations de "@filename@" */ + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ + +GType @enum_name@_get_type(void) +{ + static gsize static_g_@type@_type_id; + + if (g_once_init_enter(&static_g_@type@_type_id)) { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + {@VALUENAME@, "@VALUENAME@", "@valuenick@"}, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + + GType g_@type@_type_id = g_@type@_register_static( + g_intern_static_string("@EnumName@"), values); + + g_once_init_leave (&static_g_@type@_type_id, g_@type@_type_id); + } + + return static_g_@type@_type_id; +} + +/*** END value-tail ***/ diff --git a/src/gtkext/bindings/generated-enums.h.template b/src/gtkext/bindings/generated-enums.h.template new file mode 100644 index 0000000..58c772d --- /dev/null +++ b/src/gtkext/bindings/generated-enums.h.template @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org> + +/*** BEGIN file-header ***/ + +G_BEGIN_DECLS + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type(void) G_GNUC_CONST; +#define @ENUMPREFIX@_@ENUMSHORT@_TYPE (@enum_name@_get_type()) +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ + +G_END_DECLS + +/*** END file-tail ***/ diff --git a/src/gtkext/bufferview.c b/src/gtkext/bufferview.c index 4a700c3..13c2632 100644 --- a/src/gtkext/bufferview.c +++ b/src/gtkext/bufferview.c @@ -33,10 +33,10 @@ /* ------------------------- BASES D'UN COMPOSANT GRAPHIQUE ------------------------- */ -/* Procède à l'initialisation de l'afficheur de tampons. */ +/* Initialise la classe des afficheurs de tampons. */ static void gtk_buffer_view_class_init(GtkBufferViewClass *); -/* Procède à l'initialisation de l'afficheur de tampons. */ +/* Initialise une instance d'afficheur de tampons. */ static void gtk_buffer_view_init(GtkBufferView *); /* Supprime toutes les références externes. */ @@ -72,7 +72,7 @@ G_DEFINE_TYPE(GtkBufferView, gtk_buffer_view, GTK_TYPE_CONTENT_VIEW); * * * Paramètres : class = classe GTK à initialiser. * * * -* Description : Procède à l'initialisation de l'afficheur de tampons. * +* Description : Initialise la classe des afficheurs de tampons. * * * * Retour : - * * * @@ -146,9 +146,9 @@ static void gtk_buffer_view_class_init(GtkBufferViewClass *class) /****************************************************************************** * * -* Paramètres : display = composant GTK à initialiser. * +* Paramètres : view = composant GTK à initialiser. * * * -* Description : Procède à l'initialisation de l'afficheur de tampons. * +* Description : Initialise une instance d'afficheur de tampons. * * * * Retour : - * * * @@ -168,7 +168,7 @@ static void gtk_buffer_view_init(GtkBufferView *view) /****************************************************************************** * * -* Paramètres : display = instance d'objet GLib à traiter. * +* Paramètres : view = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -208,7 +208,7 @@ static void gtk_buffer_view_dispose(GtkBufferView *view) /****************************************************************************** * * -* Paramètres : display = instance d'objet Gtk à traiter. * +* Paramètres : view = instance d'objet Gtk à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -279,9 +279,9 @@ GBufferView *gtk_buffer_view_get_view(const GtkBufferView *view) /****************************************************************************** * * -* Paramètres : widget = composant GTK à examiner. * -* width = largeur affectée au composant graphique. * -* height = hauteur affectée au composant graphique. * +* Paramètres : widget = composant GTK à examiner. * +* width = largeur affectée au composant graphique. * +* height = hauteur affectée au composant graphique. * * baseline = ligne de base affectée au composant graphique. * * * * Description : Prend acte de la taille allouée au composant d'affichage. * diff --git a/src/gtkext/dockstation-int.h b/src/gtkext/dockstation-int.h new file mode 100644 index 0000000..17ed828 --- /dev/null +++ b/src/gtkext/dockstation-int.h @@ -0,0 +1,68 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * dockstation.h - prototypes internes pour la manipulation et l'affichage de composants rassemblés + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GTKEXT_DOCKSTATION_INT_H +#define _GTKEXT_DOCKSTATION_INT_H + + +#include "dockstation.h" + + + +/* Station de réception pour concentration d'éléments (instance) */ +struct _GtkDockStation +{ + GtkBox parent; /* A laisser en premier */ + + GtkOrientation orientation; /* Spécification d'orientation */ + + GtkWidget *tabs; /* Bascule entre panneaux */ + GtkWidget *toolbar; /* Boutons d'action */ + GtkWidget *sep1; /* Séparateur #1 */ + GtkStack *stack; /* Pile de panneaux affichés */ + GtkWidget *sep2; /* Séparateur #2 */ + + GtkTiledPanel *def_panel; /* Eventuel panneau à maintenir*/ + +}; + +/* Station de réception pour concentration d'éléments (classe) */ +struct _GtkDockStationClass +{ + GtkBoxClass parent; /* A laisser en premier */ + + /* Signaux */ + + void (* panel_docked) (GtkDockStation *, GtkTiledPanel *); + void (* panel_undocked) (GtkDockStation *, GtkTiledPanel *); + + void (* switch_widget) (GtkDockStation *, GtkWidget *); + + void (* menu_requested) (GtkDockStation *, GtkWidget *); + void (* close_requested) (GtkDockStation *, GtkWidget *); + +}; + + + +#endif /* _GTKEXT_DOCKSTATION_INT_H */ diff --git a/src/gtkext/dockstation.c b/src/gtkext/dockstation.c new file mode 100644 index 0000000..80bae75 --- /dev/null +++ b/src/gtkext/dockstation.c @@ -0,0 +1,1052 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * dockstation.c - manipulation et l'affichage de composants rassemblés + * + * Copyright (C) 2012-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "dockstation.h" + + +#include "dockstation-int.h" + + + +/* --------------------------- INTERFACE DU COMPOSANT GTK --------------------------- */ + + +/* Liste des propriétés */ + +typedef enum _DockStationProperty { + + PROP_0, /* Réservé */ + + /* Interface GtkOrientable */ + PROP_ORIENTATION, + + N_PROPERTIES = PROP_ORIENTATION + +} DockStationProperty; + +//static GParamSpec *_dock_station_properties[N_PROPERTIES] = { NULL, }; + + +/* Procède à l'initialisation de l'afficheur concentré. */ +static void gtk_dock_station_class_init(GtkDockStationClass *); + +/* Procède à l'initialisation du support d'affichage concentré. */ +static void gtk_dock_station_init(GtkDockStation *); + +/* Supprime toutes les références externes. */ +static void gtk_dock_station_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_dock_station_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Met à jour une propriété d'instance GObject. */ +static void gtk_dock_station_set_property(GObject *, guint, const GValue *, GParamSpec *); + +/* Fournit la valeur d'une propriété d'instance GObject. */ +static void gtk_dock_station_get_property(GObject *, guint, GValue *, GParamSpec *); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERFACE DU COMPOSANT GTK */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type du composant d'affichage concentré. */ +G_DEFINE_TYPE(GtkDockStation, gtk_dock_station, GTK_TYPE_GRID) + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de l'afficheur concentré. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_dock_station_class_init(GtkDockStationClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_dock_station_dispose; + object->finalize = gtk_dock_station_finalize; + object->set_property = gtk_dock_station_set_property; + object->get_property = gtk_dock_station_get_property; + + //g_object_class_install_properties(object, N_PROPERTIES, _dock_station_properties); + g_object_class_override_property(object, PROP_ORIENTATION, "orientation"); + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/dockstation.ui"); + + //gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_status_stack_on_zoom_icon_press)); + + gtk_widget_class_bind_template_child(widget, GtkDockStation, tabs); + gtk_widget_class_bind_template_child(widget, GtkDockStation, toolbar); + gtk_widget_class_bind_template_child(widget, GtkDockStation, sep1); + gtk_widget_class_bind_template_child(widget, GtkDockStation, stack); + gtk_widget_class_bind_template_child(widget, GtkDockStation, sep2); + + g_signal_new("panel-docked", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, panel_docked), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + + g_signal_new("panel-undocked", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, panel_undocked), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + + /* + g_signal_new("switch-widget", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, switch_widget), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + + g_signal_new("menu-requested", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, menu_requested), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + + g_signal_new("close-requested", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, close_requested), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + */ + +} + + +/****************************************************************************** +* * +* Paramètres : station = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation du support d'affichage concentré. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_dock_station_init(GtkDockStation *station) +{ + gtk_widget_init_template(GTK_WIDGET(station)); + + station->orientation = GTK_ORIENTATION_HORIZONTAL; + + station->def_panel = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_dock_station_dispose(GObject *object) +{ + GtkDockStation *station; /* Version spécialisée */ + + station = GTK_DOCK_STATION(object); + + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_DOCK_STATION); + + g_clear_object(&station->def_panel); + + G_OBJECT_CLASS(gtk_dock_station_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_dock_station_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_dock_station_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée un nouveau composant pour support d'affichage concentré.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkWidget *gtk_dock_station_new(void) +{ + GtkWidget *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_DOCK_STATION, NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : station = station d'accueil pour panneaux à compléter. * +* panel = nouveau panneau à afficher. * +* * +* Description : Ajoute un panneau à un groupe de tuiles. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_dock_station_add_panel(GtkDockStation *station, GtkTiledPanel *panel) +{ + GtkStackPage *page; /* Nouvelle page insérée */ + + page = gtk_stack_add_child(station->stack, GTK_WIDGET(panel)); + + if (0) // TODO + gtk_stack_page_set_title(page, "settings"); + + else + gtk_stack_page_set_icon_name(page, "logs-symbolic"); + + gtk_stack_set_visible_child(station->stack, GTK_WIDGET(panel)); + + + g_signal_emit_by_name(station, "panel-docked", panel); + + +} + + +/****************************************************************************** +* * +* Paramètres : station = station d'accueil pour panneaux à compléter. * +* panel = nouveau panneau à afficher. * +* * +* Description : Ajoute un panneau à conserver à un groupe de tuiles. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_dock_station_keep_panel(GtkDockStation *station, GtkTiledPanel *panel) +{ + bool visible; /* Visibilité des barres */ + + g_clear_object(&station->def_panel); + + if (panel != NULL) + { + station->def_panel = panel; + ref_object(panel); + + if (gtk_stack_get_page(station->stack, GTK_WIDGET(panel)) == NULL) + gtk_dock_station_add_panel(station, panel); + + } + + visible = (station->def_panel == NULL); + + gtk_widget_set_visible(station->tabs, visible); + gtk_widget_set_visible(station->toolbar, visible); + gtk_widget_set_visible(station->sep1, visible); + gtk_widget_set_visible(station->sep2, visible && station->orientation == GTK_ORIENTATION_HORIZONTAL); + +} + + +/****************************************************************************** +* * +* Paramètres : station = station d'accueil pour panneaux à consulter. * +* * +* Description : Indique si la station d'accueil contient au moins un panneau.* +* * +* Retour : true si aucun panneau n'est intégré, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_dock_station_is_empty(const GtkDockStation *station) +{ + bool result; /* Bilan à retourner */ + GtkSelectionModel *model; /* Modèle pour la pile */ + guint count; /* Nombre de panneaux présents */ + + model = gtk_stack_get_pages(station->stack); + + count = g_list_model_get_n_items(G_LIST_MODEL(model)); + + result = (count == 0); + + unref_object(model); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : station = station d'accueil pour panneaux à manipuler. * +* main = panneau principal visé par l'opération. * +* activated = nature du changement de statut : ajout, retrait ?* +* * +* Description : Note un ajout ou un retrait de panneau principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_dock_station_notify_new_main_panel_state(const GtkDockStation *station, GtkTiledPanel *main, bool activated) +{ + GListModel *list; /* Liste de pages à parcourir */ + guint count; /* Nombre de pages */ + guint i; /* Boucle de parcours */ + GtkStackPage *page; /* Page à consulter */ + GtkWidget *panel; /* Panneau intégré */ + + list = G_LIST_MODEL(gtk_stack_get_pages(station->stack)); + + count = g_list_model_get_n_items(list); + + for (i = 0; i < count; i++) + { + page = GTK_STACK_PAGE(g_list_model_get_object(list, i)); + if (page == NULL) continue; + + panel = gtk_stack_page_get_child(page); + + gtk_tiled_panel_notify_new_main_panel_state(GTK_TILED_PANEL(panel), main, activated); + + unref_object(page); + + } + + unref_object(list); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à prendre en compte. * +* pspec = définition de la propriété. * +* * +* Description : Met à jour une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_dock_station_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GtkDockStation *station; /* Version spécialisée */ + GtkOrientation orientation; /* Spécification d'orientation */ + GtkLayoutManager *layout; /* Gestionnaire de disposition */ + GtkLayoutChild *child_layout; /* Disposition de composant */ + + station = GTK_DOCK_STATION(object); + + switch (prop_id) + { + case PROP_ORIENTATION: + + orientation = g_value_get_enum(value); + + if (station->orientation != orientation) + { + station->orientation = orientation; + + gtk_orientable_set_orientation(GTK_ORIENTABLE(station->tabs), orientation); + gtk_orientable_set_orientation(GTK_ORIENTABLE(station->toolbar), orientation); + + layout = gtk_widget_get_layout_manager(GTK_WIDGET(object)); + + if (orientation == GTK_ORIENTATION_VERTICAL) + { + g_object_set(G_OBJECT(station->tabs), + "halign", GTK_ALIGN_START, + "valign", GTK_ALIGN_CENTER, + NULL); + + g_object_set(G_OBJECT(station->toolbar), + "halign", GTK_ALIGN_END, + "valign", GTK_ALIGN_CENTER, + NULL); + + child_layout = gtk_layout_manager_get_layout_child(layout, station->toolbar); + + g_object_set(G_OBJECT(child_layout), + "column", 1, + "row", 0, + NULL); + + child_layout = gtk_layout_manager_get_layout_child(layout, station->sep1); + + g_object_set(G_OBJECT(child_layout), + "column", 0, + "row", 1, + "column-span", 2, + NULL); + + gtk_widget_set_visible(station->sep2, FALSE); + + child_layout = gtk_layout_manager_get_layout_child(layout, GTK_WIDGET(station->stack)); + + g_object_set(G_OBJECT(child_layout), + "column", 0, + "row", 2, + "column-span", 2, + NULL); + + } + else + { + g_object_set(G_OBJECT(station->tabs), + "halign", GTK_ALIGN_START, + "valign", GTK_ALIGN_START, + NULL); + + g_object_set(G_OBJECT(station->toolbar), + "halign", GTK_ALIGN_END, + "valign", GTK_ALIGN_START, + NULL); + + child_layout = gtk_layout_manager_get_layout_child(layout, station->sep1); + + g_object_set(G_OBJECT(child_layout), + "column", 1, + "row", 0, + "column-span", 1, + NULL); + + child_layout = gtk_layout_manager_get_layout_child(layout, GTK_WIDGET(station->stack)); + + g_object_set(G_OBJECT(child_layout), + "column", 2, + "row", 0, + "column-span", 1, + NULL); + + gtk_widget_set_visible(station->sep2, TRUE); + + child_layout = gtk_layout_manager_get_layout_child(layout, station->sep2); + + g_object_set(G_OBJECT(child_layout), + "column", 3, + "row", 0, + NULL); + + child_layout = gtk_layout_manager_get_layout_child(layout, station->toolbar); + + g_object_set(G_OBJECT(child_layout), + "column", 4, + "row", 0, + NULL); + + } + + g_object_notify_by_pspec(object, pspec); + + } + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + + } + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à transmettre. [OUT] * +* pspec = définition de la propriété. * +* * +* Description : Fournit la valeur d'une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_dock_station_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GtkDockStation *station; /* Version spécialisée */ + + station = GTK_DOCK_STATION(object); + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum(value, station->orientation); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + + } + +} + + + + + + + + + + + + + + + + + + + + +#if 0 + +#include <malloc.h> +#include <string.h> + + +#include "easygtk.h" +#include "../core/params.h" +#include "../common/extstr.h" +#include "../glibext/chrysamarshal.h" + + + +/* Procède à l'initialisation de l'afficheur concentré. */ +static void gtk_dock_station_class_init(GtkDockStationClass *); + +/* Procède à l'initialisation du support d'affichage concentré. */ +static void gtk_dock_station_init(GtkDockStation *); + +/* Met à jour le titre du support de panneaux concentrés. */ +static gboolean gtk_dock_station_switch_panel(GtkNotebook *, gpointer *, guint, GtkDockStation *); + + + + +/* Révèle ou cache la zone de recherches. */ +static void on_toggle_revealer(GtkToggleButton *, GtkDockStation *); + +/* Demande l'apparition d'un menu pour inclure des composants. */ +static void on_click_for_menu(GtkButton *, GtkDockStation *); + +/* Demande la disparition du composant courant. */ +static void on_click_for_close(GtkButton *, GtkDockStation *); + + + + + + +/* Détermine le type du composant d'affichage concentré. */ +G_DEFINE_TYPE(GtkDockStation, gtk_dock_station, GTK_TYPE_NOTEBOOK) + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de l'afficheur concentré. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_dock_station_class_init(GtkDockStationClass *class) +{ + g_signal_new("dock-widget", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, dock_widget), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + + g_signal_new("undock-widget", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, undock_widget), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + + g_signal_new("switch-widget", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, switch_widget), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + + g_signal_new("menu-requested", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, menu_requested), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + + g_signal_new("close-requested", + GTK_TYPE_DOCK_STATION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkDockStationClass, close_requested), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + +} + + +/****************************************************************************** +* * +* Paramètres : station = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation du support d'affichage concentré. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_dock_station_init(GtkDockStation *station) +{ + GtkNotebook *notebook; /* Autre version du composant */ + GtkWidget *hbox; /* Division supérieure */ + GtkWidget *button; /* Bouton de contrôle */ + + notebook = GTK_NOTEBOOK(station); + + gtk_notebook_set_show_border(notebook, FALSE); + gtk_notebook_set_scrollable(notebook, TRUE); + + /* Définition de la zone de contrôle */ + + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_valign(hbox, GTK_ALIGN_CENTER); + gtk_widget_set_margin_end(hbox, 8); + gtk_widget_show(hbox); + + button = qck_create_toggle_button_with_named_img(G_OBJECT(station), "search", + "edit-find-symbolic", GTK_ICON_SIZE_MENU, NULL, + G_CALLBACK(on_toggle_revealer), station); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + button = qck_create_button_with_named_img(G_OBJECT(station), "menu", + "go-down-symbolic", GTK_ICON_SIZE_MENU, NULL, + G_CALLBACK(on_click_for_menu), station); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + button = qck_create_button_with_named_img(G_OBJECT(station), "close", + "window-close-symbolic", GTK_ICON_SIZE_MENU, NULL, + G_CALLBACK(on_click_for_close), station); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + gtk_notebook_set_action_widget(notebook, hbox, GTK_PACK_END); + + g_signal_connect(notebook, "switch-page", + G_CALLBACK(gtk_dock_station_switch_panel), station); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée un nouveau composant pour support d'affichage concentré.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkWidget *gtk_dock_station_new(void) +{ + return g_object_new(GTK_TYPE_DOCK_STATION, NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : notebook = support à l'origine de la mise à jour. * +* page = onglet mis en avant. * +* index = indice de l'onglet actuellement actif. * +* station = conteneur de gestion supérieur. * +* * +* Description : Met à jour le titre du support de panneaux concentrés. * +* * +* Retour : TRUE ? * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_dock_station_switch_panel(GtkNotebook *notebook, gpointer *page, guint index, GtkDockStation *station) +{ + GtkWidget *widget; /* Panneau concerné */ + GtkDockable *dockable; /* Elément encapsulé */ + GtkWidget *button; /* Bouton de contrôle */ + + widget = gtk_notebook_get_nth_page(notebook, index); + + dockable = GTK_DOCKABLE(g_object_get_data(G_OBJECT(widget), "dockable")); + + /* Mise à jour des boutons utilisables */ + + button = GTK_WIDGET(g_object_get_data(G_OBJECT(station), "search")); + + if (gtk_dockable_can_search(dockable)) + gtk_widget_show(button); + else + gtk_widget_hide(button); + + /* Remontée du changement d'onglet */ + + g_signal_emit_by_name(station, "switch-widget", widget); + + return TRUE; + +} + + +/****************************************************************************** +* * +* Paramètres : station = plateforme GTK à compléter. * +* dockable = nouvel élément à intégrer. * +* * +* Description : Ajoute un paquet d'informations à l'affichage centralisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ +#include "gtkdisplaypanel.h" +#include "../gui/panels/history.h" +void gtk_dock_station_add_dockable(GtkDockStation *station, GtkDockable *dockable) +{ + GtkWidget *widget; /* Composant GTK à intégrer */ + char *name; /* Nom à donner à l'onglet */ + char *desc; /* Description à y associer */ + int max; /* Taille maximale des titres */ + GtkWidget *label; /* Etiquette d'onglet */ + GtkNotebook *notebook; /* Autre version du composant */ + + + + + + + + /* Récupération des éléments utiles */ + + widget = gtk_dockable_build_widget(dockable); + + //widget = gtk_button_new_with_label("123"); + gtk_widget_show(widget); + + + g_object_set_data(G_OBJECT(widget), "dockable", dockable); + + name = gtk_dockable_get_name(dockable); + desc = gtk_dockable_get_desc(dockable); + + /* Mise en place de la page */ + + if (!g_generic_config_get_value(get_main_configuration(), MPK_ELLIPSIS_TAB, &max)) + max = -1; + + name = ellipsis(name, max); + label = qck_create_label(NULL, NULL, name); + free(name); + + notebook = GTK_NOTEBOOK(station); + + if (gtk_notebook_get_n_pages(notebook) > 0) + g_signal_handlers_disconnect_by_func(notebook, + G_CALLBACK(gtk_dock_station_switch_panel), station); + + gtk_notebook_append_page(notebook, widget, label); + + gtk_widget_set_tooltip_text(label, desc); + + free(desc); + + if (gtk_notebook_get_n_pages(notebook) > 1) + g_signal_connect(notebook, "switch-page", + G_CALLBACK(gtk_dock_station_switch_panel), station); + + /* Lancement des mises à jour */ + + if (gtk_notebook_get_n_pages(notebook) > 1) + gtk_notebook_set_current_page(notebook, -1); + + //g_signal_emit_by_name(station, "dock-widget", widget); + +} + + +/****************************************************************************** +* * +* Paramètres : station = plateforme GTK à compléter. * +* widget = nouvel élément à intégrer. * +* * +* Description : Change le contenu de l'onglet courant uniquement. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_dock_panel_change_active_widget(GtkDockStation *station, GtkWidget *widget) +{ + GtkNotebook *notebook; /* Autre version du composant */ + gint index; /* Indice de l'onglet actif */ + GtkWidget *old; /* Ancien composant */ + GtkWidget *label; /* Etiquette d'onglet */ + char *str; /* Titre des prochaines fois */ + + notebook = GTK_NOTEBOOK(station); + + index = gtk_notebook_get_current_page(notebook); + + g_signal_handlers_disconnect_by_func(notebook, + G_CALLBACK(gtk_dock_station_switch_panel), station); + + old = gtk_notebook_get_nth_page(notebook, index); + label = gtk_notebook_get_tab_label(notebook, old); + + g_object_ref(G_OBJECT(label)); + str = g_object_get_data(G_OBJECT(old), "title"); + + gtk_notebook_remove_page(notebook, index); + gtk_notebook_insert_page(notebook, widget, label, index); + + g_object_unref(G_OBJECT(label)); + g_object_set_data(G_OBJECT(widget), "title", str); + + gtk_notebook_set_current_page(notebook, index); + + g_signal_connect(notebook, "switch-page", + G_CALLBACK(gtk_dock_station_switch_panel), station); + +} + + +/****************************************************************************** +* * +* Paramètres : station = plateforme GTK à compléter. * +* dockable = élément existant à retirer. * +* * +* Description : Retire un paquet d'informations de l'affichage centralisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_dock_station_remove_dockable(GtkDockStation *station, GtkDockable *dockable) +{ + GtkNotebook *notebook; /* Autre version du composant */ + GtkWidget *widget; /* Composant GTK à retirer */ + gint index; /* Indice de l'onglet visé */ + + notebook = GTK_NOTEBOOK(station); + + widget = gtk_dockable_decompose(dockable, NULL); + + index = gtk_notebook_page_num(notebook, widget); + + gtk_notebook_remove_page(notebook, index); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton à l'origine de la procédure. * +* station = station d'accueil pour différents composants. * +* * +* Description : Révèle ou cache la zone de recherches. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_toggle_revealer(GtkToggleButton *button, GtkDockStation *station) +{ + GtkNotebook *notebook; /* Autre version du composant */ + gint index; /* Indice de l'onglet courant */ + GtkWidget *widget; /* Panneau concerné */ + GtkDockable *dockable; /* Elément encapsulé */ + + notebook = GTK_NOTEBOOK(station); + + index = gtk_notebook_get_current_page(notebook); + widget = gtk_notebook_get_nth_page(notebook, index); + + dockable = GTK_DOCKABLE(g_object_get_data(G_OBJECT(widget), "dockable")); + + gtk_dockable_toggle_revealer(dockable, widget, gtk_toggle_button_get_active(button)); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton à l'origine de la procédure. * +* station = station d'accueil pour différents composants. * +* * +* Description : Demande l'apparition d'un menu pour inclure des composants. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_click_for_menu(GtkButton *button, GtkDockStation *station) +{ + g_signal_emit_by_name(station, "menu-requested", button); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton à l'origine de la procédure. * +* station = station d'accueil pour différents composants. * +* * +* Description : Demande la disparition du composant courant. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_click_for_close(GtkButton *button, GtkDockStation *station) +{ + g_signal_emit_by_name(station, "close-requested", button); + +} + + +#endif + diff --git a/src/gtkext/gtkdockstation.h b/src/gtkext/dockstation.h index f286c1c..e4c849f 100644 --- a/src/gtkext/gtkdockstation.h +++ b/src/gtkext/dockstation.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * gtkdockstation.h - prototypes pour la manipulation et l'affichage de composants rassemblés + * dockstation.h - prototypes pour la manipulation et l'affichage de composants rassemblés * - * Copyright (C) 2012-2018 Cyrille Bagard + * Copyright (C) 2012-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,13 +21,46 @@ */ -#ifndef _GTKEXT_GTKDOCKSTATION_H -#define _GTKEXT_GTKDOCKSTATION_H +#ifndef _GTKEXT_DOCKSTATION_H +#define _GTKEXT_DOCKSTATION_H #include <gtk/gtk.h> +#include "panel.h" +#include "../glibext/helpers.h" + + + +#define GTK_TYPE_DOCK_STATION (gtk_dock_station_get_type()) + +DECLARE_GTYPE(GtkDockStation, gtk_dock_station, GTK, DOCK_STATION); + + +/* Crée un nouveau composant pour support d'affichage concentré. */ +GtkWidget *gtk_dock_station_new(void); + +/* Ajoute un panneau à un groupe de tuiles. */ +void gtk_dock_station_add_panel(GtkDockStation *, GtkTiledPanel *); + +/* Ajoute un panneau à conserver à un groupe de tuiles. */ +void gtk_dock_station_keep_panel(GtkDockStation *, GtkTiledPanel *); + +/* Indique si la station d'accueil contient au moins un panneau. */ +bool gtk_dock_station_is_empty(const GtkDockStation *); + +/* Note un ajout ou un retrait de panneau principal. */ +void gtk_dock_station_notify_new_main_panel_state(const GtkDockStation *, GtkTiledPanel *, bool); + + + + +#if 0 + +#include <gtk/gtk.h> + + #include "gtkdockable.h" @@ -76,9 +109,6 @@ struct _GtkDockStationClass /* Détermine le type du composant d'affichage concentré. */ GType gtk_dock_station_get_type(void); -/* Crée un nouveau composant pour support d'affichage concentré. */ -GtkWidget *gtk_dock_station_new(void); - /* Ajoute un paquet d'informations à l'affichage centralisé. */ void gtk_dock_station_add_dockable(GtkDockStation *, GtkDockable *); @@ -92,6 +122,7 @@ void gtk_dock_station_remove_dockable(GtkDockStation *, GtkDockable *); //G_END_DECLS +#endif -#endif /* _GTKEXT_GTKDOCKSTATION_H */ +#endif /* _GTKEXT_DOCKSTATION_H */ diff --git a/src/gtkext/dockstation.ui b/src/gtkext/dockstation.ui new file mode 100644 index 0000000..4d25134 --- /dev/null +++ b/src/gtkext/dockstation.ui @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="GtkDockStation" parent="GtkGrid"> + <property name="orientation">horizontal</property> + + <child> + <object class="GtkStackSwitcher" id="tabs"> + <property name="orientation">horizontal</property> + <property name="halign">start</property> + <property name="valign">start</property> + <property name="margin-start">2</property> + <property name="margin-top">2</property> + <property name="margin-end">2</property> + <property name="margin-bottom">2</property> + <property name="stack">stack</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + + <child> + <object class="GtkBox" id="toolbar"> + <property name="orientation">horizontal</property> + <property name="halign">end</property> + <property name="valign">start</property> + + <child> + <object class="GtkButton"> + <property name="icon-name">window-close-symbolic</property> + <style> + <class name="circular"/> + <class name="flat"/> + <class name="control-button"/> + </style> + </object> + </child> + + <layout> + <property name="column">4</property> + <property name="row">0</property> + </layout> + </object> + </child> + + <child> + <object class="GtkSeparator" id="sep1"> + <property name="orientation">horizontal</property> + <layout> + <property name="column">1</property> + <property name="row">0</property> + <property name="column-span">1</property> + </layout> + </object> + </child> + + <child> + <object class="GtkStack" id="stack"> + <property name="hexpand">true</property> + <layout> + <property name="column">2</property> + <property name="row">0</property> + <property name="column-span">1</property> + </layout> + </object> + </child> + + <child> + <object class="GtkSeparator" id="sep2"> + <property name="orientation">horizontal</property> + <layout> + <property name="column">3</property> + <property name="row">0</property> + </layout> + </object> + </child> + + </template> +</interface> diff --git a/src/gtkext/gresource.xml b/src/gtkext/gresource.xml index 225b2a4..26943a6 100644 --- a/src/gtkext/gresource.xml +++ b/src/gtkext/gresource.xml @@ -1,7 +1,17 @@ <?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/re/chrysalide/framework/gtkext"> + <file compressed="true">dockstation.ui</file> + <file compressed="true">grid.ui</file> <file compressed="true">hexview.css</file> <file compressed="true">hexview.ui</file> + <file compressed="true">launcher.ui</file> + <file compressed="true">statusstack.ui</file> + <file compressed="true">tweak.ui</file> + </gresource> + <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions"> + <file compressed="true" alias="nolock-symbolic.svg">../../data/images/nolock-symbolic.svg</file> + <file compressed="true" alias="locked-symbolic.svg">../../data/images/locked-symbolic.svg</file> + <file compressed="true" alias="unlocked-symbolic.svg">../../data/images/unlocked-symbolic.svg</file> </gresource> </gresources> diff --git a/src/gtkext/grid-int.h b/src/gtkext/grid-int.h new file mode 100644 index 0000000..8a2702b --- /dev/null +++ b/src/gtkext/grid-int.h @@ -0,0 +1,81 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * grid-int.h - prototypes interne pour un composant d'affichage avec des chemins vers les composants contenus + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GTKEXT_GRID_INT_H +#define _GTKEXT_GRID_INT_H + + +#include "dockstation.h" +#include "grid.h" + + + +/* Conteneur pour un affichage en tuiles nommées (instance) */ +struct _GtkTilingGrid +{ + GtkGrid parent; /* A laisser en premier */ + + GSettings *settings; /* Configuration du conteneur */ + + LayoutReachOptions layout; /* Disposition générale */ + + bool visible[TGB_COUNT]; /* Visibilités souhaitées */ + + GtkRevealer *top; /* Zone d'accueil #1 */ + GtkWidget *top_handle; /* Poignée de redimensionnement*/ + GtkDockStation *top_station; /* Station entière */ + + GtkRevealer *left; /* Zone d'accueil #2 */ + GtkWidget *left_handle; /* Poignée de redimensionnement*/ + GtkDockStation *left_station; /* Station entière */ + + GtkDockStation *main_station; /* Zone d'accueil #3 */ + + GtkRevealer *right; /* Zone d'accueil #4 */ + GtkWidget *right_handle; /* Poignée de redimensionnement*/ + GtkDockStation *right_station; /* Station entière */ + + GtkRevealer *bottom; /* Zone d'accueil #5 */ + GtkWidget *bottom_handle; /* Poignée de redimensionnement*/ + GtkDockStation *bottom_station; /* Station entière */ + + GtkGesture *tpad_gesture[TGB_COUNT]; /* Gestionnaires du touchpad */ + + bool panning; /* Redimensionnement en cours */ + +}; + +/* Conteneur pour un affichage en tuiles nommées (classe) */ +struct _GtkTilingGridClass +{ + GtkGridClass parent; /* A laisser en premier */ + + /* Signaux */ + + void (* station_created) (GtkTilingGrid *, GtkDockStation *, gpointer); + +}; + + + +#endif /* _GTKEXT_GRID_INT_H */ diff --git a/src/gtkext/grid.c b/src/gtkext/grid.c new file mode 100644 index 0000000..eb3cdf9 --- /dev/null +++ b/src/gtkext/grid.c @@ -0,0 +1,1128 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * grid.c - composant d'affichage avec des chemins vers les composants contenus + * + * Copyright (C) 2018-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "grid.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include "grid-int.h" +#include "helpers.h" +#include "bindings/grid-enums.h" + + + +/* --------------------------- INTERFACE DU COMPOSANT GTK --------------------------- */ + + +/* Liste des propriétés */ + +typedef enum _TilingGridProperty { + + PROP_0, /* Réservé */ + + PROP_LAYOUT, /* Disposition générale */ + + PROP_EMPTY_TOP, /* Vide de la zone supérieure */ + PROP_EMPTY_LEFT, /* Vide de la zone de gauche */ + PROP_EMPTY_RIGHT, /* Vide de la zone de droite */ + PROP_EMPTY_BOTTOM, /* Vide de la zone inférieure */ + + PROP_VISIBLE_TOP, /* Visibilité de zone sup. */ + PROP_VISIBLE_LEFT, /* Visibilité de zone de gauche*/ + PROP_VISIBLE_RIGHT, /* Visibilité de zone de droite*/ + PROP_VISIBLE_BOTTOM, /* Visibilité de zone inf. */ + + N_PROPERTIES + +} TilingGridProperty; + +static GParamSpec *_tiling_grid_properties[N_PROPERTIES] = { NULL, }; + + +/* Initialise la classe des conteneurs d'affichage en tuiles. */ +static void gtk_tiling_grid_class_init(GtkTilingGridClass *); + +/* Initialise une instance de conteneur d'affichage en tuiles. */ +static void gtk_tiling_grid_init(GtkTilingGrid *); + +/* Supprime toutes les références externes. */ +static void gtk_tiling_grid_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_tiling_grid_finalize(GObject *); + +/* Réagit à une intégration ou à un retrait de panneau. */ +static void gtk_tiling_grid_on_panel_un_docked(GtkDockStation *, GtkTiledPanel *, GtkTilingGrid *); + + + +/* -------------------- REDIMENSIONNEMENT DE ZONES POUR PANNEAUX -------------------- */ + + +/* Initie un redimensionnement par drag-and-drop. */ +static void gtk_tiling_grid_on_gesture_drag_begin(GtkGestureDrag *, double, double, GtkTilingGrid *); + +/* Applique l'effet d'un redimensionnement drag-and-drop donné. */ +static void gtk_tiling_grid_on_gesture_drag_update(GtkTilingGrid *, TilingGridBorder, double, double); + +/* Actualise l'effet d'un redimensionnement drag-and-drop. */ +static void gtk_tiling_grid_on_top_gesture_drag_update(GtkGestureDrag *, double, double, GtkTilingGrid *); + +/* Actualise l'effet d'un redimensionnement drag-and-drop. */ +static void gtk_tiling_grid_on_left_gesture_drag_update(GtkGestureDrag *, double, double, GtkTilingGrid *); + +/* Actualise l'effet d'un redimensionnement drag-and-drop. */ +static void gtk_tiling_grid_on_right_gesture_drag_update(GtkGestureDrag *, double, double, GtkTilingGrid *); + +/* Actualise l'effet d'un redimensionnement drag-and-drop. */ +static void gtk_tiling_grid_on_bottom_gesture_drag_update(GtkGestureDrag *, double, double, GtkTilingGrid *); + +/* Clôture un drag-and-drop de redimensionnement. */ +static void gtk_tiling_grid_on_gesture_drag_end(GtkGestureDrag *, double, double, GtkTilingGrid *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Met à jour une propriété d'instance GObject. */ +static void gtk_tiling_grid_set_property(GObject *, guint, const GValue *, GParamSpec *); + +/* Fournit la valeur d'une propriété d'instance GObject. */ +static void gtk_tiling_grid_get_property(GObject *, guint, GValue *, GParamSpec *); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERFACE DU COMPOSANT GTK */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type du conteneur d'affichage en tuiles nommées. */ +G_DEFINE_TYPE(GtkTilingGrid, gtk_tiling_grid, GTK_TYPE_GRID) + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des conteneurs d'affichage en tuiles. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_class_init(GtkTilingGridClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_tiling_grid_dispose; + object->finalize = gtk_tiling_grid_finalize; + object->set_property = gtk_tiling_grid_set_property; + object->get_property = gtk_tiling_grid_get_property; + + _tiling_grid_properties[PROP_LAYOUT] = + g_param_spec_flags("layout", NULL, NULL, + LAYOUT_REACH_OPTIONS_TYPE, + LRO_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + _tiling_grid_properties[PROP_EMPTY_TOP] = + g_param_spec_boolean("empty-top", NULL, NULL, + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + _tiling_grid_properties[PROP_EMPTY_LEFT] = + g_param_spec_boolean("empty-left", NULL, NULL, + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + _tiling_grid_properties[PROP_EMPTY_RIGHT] = + g_param_spec_boolean("empty-right", NULL, NULL, + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + _tiling_grid_properties[PROP_EMPTY_BOTTOM] = + g_param_spec_boolean("empty-bottom", NULL, NULL, + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * La valeur initiale des champs suivants est à maintenir synchronisée avec + * les initialisations de gtk_tiling_grid_init(). + */ + + _tiling_grid_properties[PROP_VISIBLE_TOP] = + g_param_spec_boolean("visible-top", NULL, NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + _tiling_grid_properties[PROP_VISIBLE_LEFT] = + g_param_spec_boolean("visible-left", NULL, NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + _tiling_grid_properties[PROP_VISIBLE_RIGHT] = + g_param_spec_boolean("visible-right", NULL, NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + _tiling_grid_properties[PROP_VISIBLE_BOTTOM] = + g_param_spec_boolean("visible-bottom", NULL, NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties(object, N_PROPERTIES, _tiling_grid_properties); + + widget = GTK_WIDGET_CLASS(class); + + g_type_ensure(GTK_TYPE_DOCK_STATION); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/grid.ui"); + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_tiling_grid_on_panel_un_docked)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_tiling_grid_on_gesture_drag_begin)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_tiling_grid_on_top_gesture_drag_update)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_tiling_grid_on_left_gesture_drag_update)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_tiling_grid_on_right_gesture_drag_update)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_tiling_grid_on_bottom_gesture_drag_update)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_tiling_grid_on_gesture_drag_end)); + + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, top); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, top_handle); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, top_station); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, left); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, left_handle); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, left_station); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, main_station); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, right); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, right_handle); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, right_station); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, bottom); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, bottom_handle); + gtk_widget_class_bind_template_child(widget, GtkTilingGrid, bottom_station); + + + ///////////// + + g_signal_new("station-created", + GTK_TYPE_TILING_GRID, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkTilingGridClass, station_created), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GTK_TYPE_WIDGET/*DOCK_STATION FIXME */); + +} + + +/****************************************************************************** +* * +* Paramètres : grid = instance GTK à initialiser. * +* * +* Description : Initialise une instance de conteneur d'affichage en tuiles. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_init(GtkTilingGrid *grid) +{ + gtk_widget_init_template(GTK_WIDGET(grid)); + + grid->settings = g_settings_new_with_path("re.chrysalide.framework.tiledgrid", + "/re/chrysalide/framework/gui/tiles/"); + + g_settings_bind(grid->settings, "layout", + grid, "layout", + G_SETTINGS_BIND_DEFAULT); + + /** + * L'initialisation des champs suivants est à maintenir synchronisée avec + * les valeurs initiales de gtk_tiling_grid_class_init(). + */ + + grid->visible[TGB_TOP] = true; + grid->visible[TGB_LEFT] = true; + grid->visible[TGB_RIGHT] = true; + grid->visible[TGB_BOTTOM] = true; + + g_settings_bind(grid->settings, "top-request", + grid->top_station, "height-request", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind(grid->settings, "top-visibility", + grid, "visible-top", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind(grid->settings, "left-request", + grid->left_station, "width-request", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind(grid->settings, "left-visibility", + grid, "visible-left", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind(grid->settings, "right-request", + grid->right_station, "width-request", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind(grid->settings, "right-visibility", + grid, "visible-right", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind(grid->settings, "bottom-request", + grid->bottom_station, "height-request", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind(grid->settings, "bottom-visibility", + grid, "visible-bottom", + G_SETTINGS_BIND_DEFAULT); + + gtk_widget_set_cursor_from_name(grid->top_handle, "row-resize"); + gtk_widget_set_cursor_from_name(grid->left_handle, "col-resize"); + gtk_widget_set_cursor_from_name(grid->right_handle, "col-resize"); + gtk_widget_set_cursor_from_name(grid->bottom_handle, "row-resize"); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_dispose(GObject *object) +{ + GtkTilingGrid *grid; /* Version spécialisée */ + + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_DOCK_STATION); + + grid = GTK_TILING_GRID(object); + + g_clear_object(&grid->settings); + + G_OBJECT_CLASS(gtk_tiling_grid_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_tiling_grid_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée une nouvelle instance de conteneur avec tuiles. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkWidget *gtk_tiling_grid_new(void) +{ + GtkWidget *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_TILING_GRID, NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : grid = zone d'affichage en tuiles à manipuler. * +* border = sélection de la zone à considérer. * +* visible = nouveau statut de visibilité à appliquer. * +* * +* Description : Affiche ou masque une zone du conteneur en tuiles. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_tiling_grid_set_visible(GtkTilingGrid *grid, TilingGridBorder border, bool visible) +{ + GtkRevealer *revealer; /* Cible visée par l'opération */ + GtkDockStation *station; /* Apport d'une contrainte */ + + if (grid->visible[border] != visible) + { + grid->visible[border] = visible; + + switch (border) + { + case TGB_TOP: + revealer = grid->top; + station = grid->top_station; + break; + + case TGB_LEFT: + revealer = grid->left; + station = grid->left_station; + break; + + case TGB_RIGHT: + revealer = grid->right; + station = grid->right_station; + break; + + case TGB_BOTTOM: + revealer = grid->bottom; + station = grid->bottom_station; + break; + + } + + gtk_revealer_set_reveal_child(revealer, visible && !gtk_dock_station_is_empty(station)); + + g_object_notify_by_pspec(G_OBJECT(grid), _tiling_grid_properties[PROP_VISIBLE_TOP + border]); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : grid = zone d'affichage en tuiles à manipuler. * +* border = sélection de la zone à considérer. * +* * +* Description : Fournit la visibilité d'une zone du conteneur en tuiles. * +* * +* Retour : Visibilité de la zone considérée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_tiling_grid_get_visible(GtkTilingGrid *grid, TilingGridBorder border) +{ + bool result; /* Statut à retourner */ + + result = grid->visible[border]; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : grid = zone d'affichage en tuiles à manipuler. * +* panel = nouveau panneau à afficher. * +* keep = indique si le panneau est à conserver présent. * +* * +* Description : Ajoute un panneau à un conteneur en tuiles. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_tiling_grid_add_panel(GtkTilingGrid *grid, GtkTiledPanel *panel, bool keep) +{ + char *path; /* Chemin visé par le panneau */ + bool static_path; /* Nature du chemin à traiter */ + + path = gtk_tiled_panel_get_path(panel); + + static_path = (path == NULL); + + if (static_path) + path = ""; + + switch (path[0]) + { + case 'N': + if (keep) + gtk_dock_station_keep_panel(grid->top_station, panel); + else + gtk_dock_station_add_panel(grid->top_station, panel); + break; + + case 'W': + if (keep) + gtk_dock_station_keep_panel(grid->left_station, panel); + else + gtk_dock_station_add_panel(grid->left_station, panel); + break; + + case '\0': + case 'M': + if (keep) + gtk_dock_station_keep_panel(grid->main_station, panel); + else + gtk_dock_station_add_panel(grid->main_station, panel); + break; + + case 'E': + if (keep) + gtk_dock_station_keep_panel(grid->right_station, panel); + else + gtk_dock_station_add_panel(grid->right_station, panel); + break; + + case 'S': + if (keep) + gtk_dock_station_keep_panel(grid->bottom_station, panel); + else + gtk_dock_station_add_panel(grid->bottom_station, panel); + break; + + default: + break; + + } + + if (!static_path) + free(path); + +} + + +/****************************************************************************** +* * +* Paramètres : grid = zone d'affichage en tuiles à manipuler. * +* main = panneau principal visé par l'opération. * +* activated = nature du changement de statut : ajout, retrait ?* +* * +* Description : Note un ajout ou un retrait de panneau principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_tiling_grid_notify_new_main_panel_state(const GtkTilingGrid *grid, GtkTiledPanel *main, bool activated) +{ + gtk_dock_station_notify_new_main_panel_state(grid->top_station, main, activated); + + gtk_dock_station_notify_new_main_panel_state(grid->left_station, main, activated); + + gtk_dock_station_notify_new_main_panel_state(grid->main_station, main, activated); + + gtk_dock_station_notify_new_main_panel_state(grid->right_station, main, activated); + + gtk_dock_station_notify_new_main_panel_state(grid->bottom_station, main, activated); + +} + + +/****************************************************************************** +* * +* Paramètres : station = plateforme GTK ayant connu un changement. * +* widget = nouvel élément à intégrer. * +* grid = gestionnaire de placement en tuile concerné. * +* * +* Description : Réagit à une intégration ou à un retrait de panneau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_on_panel_un_docked(GtkDockStation *station, GtkTiledPanel *panel, GtkTilingGrid *grid) +{ + TilingGridBorder border; /* Aire concernée par l'action */ + GtkRevealer *revealer; /* Cible visée par l'opération */ + bool new_state; /* Nouveau statut d'affichage */ + + if (station == grid->top_station) + { + border = TGB_TOP; + revealer = grid->top; + } + + else if (station == grid->left_station) + { + border = TGB_LEFT; + revealer = grid->left; + } + + else if (station == grid->right_station) + { + border = TGB_RIGHT; + revealer = grid->right; + } + + else if (station == grid->bottom_station) + { + border = TGB_BOTTOM; + revealer = grid->bottom; + } + + else + assert(false); + + new_state = grid->visible[border] && !gtk_dock_station_is_empty(station); + + if (gtk_revealer_get_reveal_child(revealer) != new_state) + gtk_revealer_set_reveal_child(revealer, new_state); + + /** + * On ne sait pas si l'état a réellement changé, mais on avertit + * d'une mise à jour quand même, au cas où. + */ + + g_object_notify_by_pspec(G_OBJECT(grid), _tiling_grid_properties[PROP_EMPTY_TOP + border]); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* REDIMENSIONNEMENT DE ZONES POUR PANNEAUX */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : gesture = encadrement de déplacement à l'origine de l'appel. * +* start_x = pointe de départ sur l'axe des abscisses. * +* start_y = pointe de départ sur l'axe des ordonnées. * +* grid = gestionnaire de placement en tuile concerné. * +* * +* Description : Initie un redimensionnement par drag-and-drop. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_on_gesture_drag_begin(GtkGestureDrag *gesture, double start_x, double start_y, GtkTilingGrid *grid) +{ + gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_CLAIMED); + + grid->panning = true; + +} + + +/****************************************************************************** +* * +* Paramètres : grid = zone d'affichage en tuiles à manipuler. * +* border = sélection de la zone à considérer. * +* offset_x = déplacement sur l'axe des abscisses. * +* offset_y = déplacement sur l'axe des ordonnées. * +* * +* Description : Applique l'effet d'un redimensionnement drag-and-drop donné. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_on_gesture_drag_update(GtkTilingGrid *grid, TilingGridBorder border, double offset_x, double offset_y) +{ + GtkDockStation *station; /* Station entière */ + int request; /* Taille requise */ + + /* Sélection de la poignée adaptée */ + + switch (border) + { + case TGB_TOP: + station = grid->top_station; + break; + + case TGB_LEFT: + station = grid->left_station; + break; + + case TGB_RIGHT: + station = grid->right_station; + break; + + case TGB_BOTTOM: + station = grid->bottom_station; + break; + + } + + /* Détermination d'une nouvelle position et application */ + + switch (border) + { + case TGB_TOP: + case TGB_BOTTOM: + g_object_get(G_OBJECT(station), "height-request", &request, NULL); + break; + + case TGB_LEFT: + case TGB_RIGHT: + g_object_get(G_OBJECT(station), "width-request", &request, NULL); + break; + + } + + switch (border) + { + case TGB_TOP: + request += offset_y; + break; + + case TGB_LEFT: + request += offset_x; + break; + + case TGB_RIGHT: + request += -offset_x; + break; + + case TGB_BOTTOM: + request += -offset_y; + break; + + } + + if (request > 0) + { + switch (border) + { + case TGB_TOP: + case TGB_BOTTOM: + g_object_set(G_OBJECT(station), "height-request", request, NULL); + break; + + case TGB_LEFT: + case TGB_RIGHT: + g_object_set(G_OBJECT(station), "width-request", request, NULL); + break; + + } + + } + +} + + +/****************************************************************************** +* * +* Paramètres : gesture = encadrement de déplacement à l'origine de l'appel. * +* offset_x = déplacement sur l'axe des abscisses. * +* offset_y = déplacement sur l'axe des ordonnées. * +* grid = gestionnaire de placement en tuile concerné. * +* * +* Description : Actualise l'effet d'un redimensionnement drag-and-drop. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_on_top_gesture_drag_update(GtkGestureDrag *gesture, double offset_x, double offset_y, GtkTilingGrid *grid) +{ + gtk_tiling_grid_on_gesture_drag_update(grid, TGB_TOP, offset_x, offset_y); + +} + + +/****************************************************************************** +* * +* Paramètres : gesture = encadrement de déplacement à l'origine de l'appel. * +* offset_x = déplacement sur l'axe des abscisses. * +* offset_y = déplacement sur l'axe des ordonnées. * +* grid = gestionnaire de placement en tuile concerné. * +* * +* Description : Actualise l'effet d'un redimensionnement drag-and-drop. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_on_left_gesture_drag_update(GtkGestureDrag *gesture, double offset_x, double offset_y, GtkTilingGrid *grid) +{ + gtk_tiling_grid_on_gesture_drag_update(grid, TGB_LEFT, offset_x, offset_y); + +} + + +/****************************************************************************** +* * +* Paramètres : gesture = encadrement de déplacement à l'origine de l'appel. * +* offset_x = déplacement sur l'axe des abscisses. * +* offset_y = déplacement sur l'axe des ordonnées. * +* grid = gestionnaire de placement en tuile concerné. * +* * +* Description : Actualise l'effet d'un redimensionnement drag-and-drop. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_on_right_gesture_drag_update(GtkGestureDrag *gesture, double offset_x, double offset_y, GtkTilingGrid *grid) +{ + gtk_tiling_grid_on_gesture_drag_update(grid, TGB_RIGHT, offset_x, offset_y); + +} + + +/****************************************************************************** +* * +* Paramètres : gesture = encadrement de déplacement à l'origine de l'appel. * +* offset_x = déplacement sur l'axe des abscisses. * +* offset_y = déplacement sur l'axe des ordonnées. * +* grid = gestionnaire de placement en tuile concerné. * +* * +* Description : Actualise l'effet d'un redimensionnement drag-and-drop. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_on_bottom_gesture_drag_update(GtkGestureDrag *gesture, double offset_x, double offset_y, GtkTilingGrid *grid) +{ + gtk_tiling_grid_on_gesture_drag_update(grid, TGB_BOTTOM, offset_x, offset_y); + +} + + +/****************************************************************************** +* * +* Paramètres : gesture = encadrement de déplacement à l'origine de l'appel. * +* offset_x = déplacement final sur l'axe des abscisses. * +* offset_y = déplacement final sur l'axe des ordonnées. * +* grid = gestionnaire de placement en tuile concerné. * +* * +* Description : Clôture un drag-and-drop de redimensionnement. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_on_gesture_drag_end(GtkGestureDrag *gesture, double offset_x, double offset_y, GtkTilingGrid *grid) +{ + if (!grid->panning) + gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_DENIED); + else + grid->panning = false; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à prendre en compte. * +* pspec = définition de la propriété. * +* * +* Description : Met à jour une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GtkTilingGrid *grid; /* Version spécialisée */ + + grid = GTK_TILING_GRID(object); + + switch (prop_id) + { + case PROP_LAYOUT: + if (grid->layout != g_value_get_flags(value)) + { + grid->layout = g_value_get_flags(value); + + apply_tiling_grid_layout(GTK_GRID(grid), grid->layout, (GtkWidget *[]) { + GTK_WIDGET(grid->top), GTK_WIDGET(grid->left), + GTK_WIDGET(grid->right), GTK_WIDGET(grid->bottom) + }); + + g_object_notify_by_pspec(object, pspec); + + } + break; + + gtk_tiling_grid_set_visible(grid, TGB_TOP, g_value_get_boolean(value)); + break; + + case PROP_VISIBLE_TOP: + gtk_tiling_grid_set_visible(grid, TGB_TOP, g_value_get_boolean(value)); + break; + + case PROP_VISIBLE_LEFT: + gtk_tiling_grid_set_visible(grid, TGB_LEFT, g_value_get_boolean(value)); + break; + + case PROP_VISIBLE_RIGHT: + gtk_tiling_grid_set_visible(grid, TGB_RIGHT, g_value_get_boolean(value)); + break; + + case PROP_VISIBLE_BOTTOM: + gtk_tiling_grid_set_visible(grid, TGB_BOTTOM, g_value_get_boolean(value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + + } + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à transmettre. [OUT] * +* pspec = définition de la propriété. * +* * +* Description : Fournit la valeur d'une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiling_grid_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GtkTilingGrid *grid; /* Version spécialisée */ + + grid = GTK_TILING_GRID(object); + + switch (prop_id) + { + case PROP_EMPTY_TOP: + g_value_set_boolean(value, gtk_dock_station_is_empty(grid->top_station)); + break; + + case PROP_EMPTY_LEFT: + g_value_set_boolean(value, gtk_dock_station_is_empty(grid->left_station)); + break; + + case PROP_EMPTY_RIGHT: + g_value_set_boolean(value, gtk_dock_station_is_empty(grid->right_station)); + break; + + case PROP_EMPTY_BOTTOM: + g_value_set_boolean(value, gtk_dock_station_is_empty(grid->bottom_station)); + break; + + case PROP_VISIBLE_TOP: + g_value_set_boolean(value, gtk_tiling_grid_get_visible(grid, TGB_TOP)); + break; + + case PROP_VISIBLE_LEFT: + g_value_set_boolean(value, gtk_tiling_grid_get_visible(grid, TGB_LEFT)); + break; + + case PROP_VISIBLE_RIGHT: + g_value_set_boolean(value, gtk_tiling_grid_get_visible(grid, TGB_RIGHT)); + break; + + case PROP_VISIBLE_BOTTOM: + g_value_set_boolean(value, gtk_tiling_grid_get_visible(grid, TGB_BOTTOM)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + + } + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* FORME GENERIQUE DE MISE EN DISPOSITION */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : grid = composant GTK dont le contenu est à arranger. * +* options = options de mise en place. * +* panels = liste organisée de composants à déplacer. * +* * +* Description : Met en place une disposition particulière de panneaux. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void apply_tiling_grid_layout(GtkGrid *grid, LayoutReachOptions options, GtkWidget *panels[TGB_COUNT]) +{ + int top_panel_span; /* Etendue d'un composant #1 */ + int left_panel_span; /* Etendue d'un composant #2 */ + int right_panel_span; /* Etendue d'un composant #3 */ + int bottom_panel_span; /* Etendue d'un composant #4 */ + int top_panel_column; /* Position de composant #1 */ + int left_panel_row; /* Position de composant #2 */ + int right_panel_row; /* Position de composant #3 */ + int bottom_panel_column; /* Position de composant #4 */ + GtkLayoutManager *layout; /* Gestionnaire de disposition */ + GtkGridLayoutChild *top_panel_layout; /* Disposition de composant #1 */ + GtkGridLayoutChild *left_panel_layout; /* Disposition de composant #2 */ + GtkGridLayoutChild *right_panel_layout; /* Disposition de composant #3 */ + GtkGridLayoutChild *bottom_panel_layout;/* Disposition de composant #4 */ + + /* Calcul des placements */ + + top_panel_span = 3; + left_panel_span = 3; + right_panel_span = 3; + bottom_panel_span = 3; + + if (options & LRO_LEFT_TOP_REACH) + { + top_panel_column = 1; + top_panel_span--; + left_panel_row = 0; + } + else + { + top_panel_column = 0; + left_panel_row = 1; + left_panel_span--; + } + + if (options & LRO_LEFT_BOTTOM_REACH) + { + bottom_panel_column = 1; + bottom_panel_span--; + } + else + { + left_panel_span--; + bottom_panel_column = 0; + } + + if (options & LRO_RIGHT_TOP_REACH) + { + top_panel_span--; + right_panel_row = 0; + } + else + { + right_panel_row = 1; + right_panel_span--; + } + + if (options & LRO_RIGHT_BOTTOM_REACH) + bottom_panel_span--; + else + right_panel_span--; + + /* Mise en application des contraintes */ + + layout = gtk_widget_get_layout_manager(GTK_WIDGET(grid)); + + top_panel_layout = GTK_GRID_LAYOUT_CHILD(gtk_layout_manager_get_layout_child(layout, panels[TGB_TOP])); + left_panel_layout = GTK_GRID_LAYOUT_CHILD(gtk_layout_manager_get_layout_child(layout, panels[TGB_LEFT])); + right_panel_layout = GTK_GRID_LAYOUT_CHILD(gtk_layout_manager_get_layout_child(layout, panels[TGB_RIGHT])); + bottom_panel_layout = GTK_GRID_LAYOUT_CHILD(gtk_layout_manager_get_layout_child(layout, panels[TGB_BOTTOM])); + + g_object_set(G_OBJECT(top_panel_layout), + "column", top_panel_column, + "column-span", top_panel_span, + NULL); + + g_object_set(G_OBJECT(left_panel_layout), + "row", left_panel_row, + "row-span", left_panel_span, + NULL); + + g_object_set(G_OBJECT(right_panel_layout), + "row", right_panel_row, + "row-span", right_panel_span, + NULL); + + g_object_set(G_OBJECT(bottom_panel_layout), + "column", bottom_panel_column, + "column-span", bottom_panel_span, + NULL); + + gtk_layout_manager_layout_changed(layout); + +} diff --git a/src/gtkext/tiledgrid.h b/src/gtkext/grid.h index 539e248..fd98035 100644 --- a/src/gtkext/tiledgrid.h +++ b/src/gtkext/grid.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * tiledgrid.h - prototypes pour un composant d'affichage avec des chemins vers les composants contenus * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,10 +21,81 @@ */ -#ifndef _GTKEXT_TILEDGRID_H -#define _GTKEXT_TILEDGRID_H +#ifndef _GTKEXT_GRID_H +#define _GTKEXT_GRID_H +#include <stdbool.h> +#include <gtk/gtk.h> + + +#include "panel.h" +#include "../glibext/helpers.h" + + + +#define GTK_TYPE_TILING_GRID (gtk_tiling_grid_get_type()) + +DECLARE_GTYPE(GtkTilingGrid, gtk_tiling_grid, GTK, TILING_GRID); + + +/* Crée une nouvelle instance de conteneur avec tuiles. */ +GtkWidget *gtk_tiling_grid_new(void); + +/* Liste des zones de bordure */ +typedef enum _TilingGridBorder /*< skip (glib-mkenums) >*/ +{ + TGB_TOP, /* Zone supérieure */ + TGB_LEFT, /* Zone de gauche */ + TGB_RIGHT, /* Zone de droite */ + TGB_BOTTOM, /* Zone inférieure */ + +} TilingGridBorder; + +/** + * Fixe le nombre de combinaisons sans le rendre visible dans l'énumération. + */ +#define TGB_COUNT (TGB_BOTTOM + 1) + +/* Affiche ou masque une zone du conteneur en tuiles. */ +void gtk_tiling_grid_set_visible(GtkTilingGrid *, TilingGridBorder, bool); + +/* Fournit la visibilité d'une zone du conteneur en tuiles. */ +bool gtk_tiling_grid_get_visible(GtkTilingGrid *, TilingGridBorder); + +/* Ajoute un panneau à un conteneur en tuiles. */ +void gtk_tiling_grid_add_panel(GtkTilingGrid *, GtkTiledPanel *, bool); + +/* Note un ajout ou un retrait de panneau principal. */ +void gtk_tiling_grid_notify_new_main_panel_state(const GtkTilingGrid *, GtkTiledPanel *, bool); + + + +/* --------------------- FORME GENERIQUE DE MISE EN DISPOSITION --------------------- */ + + +/* Options de dispositions cumulables */ +typedef enum _LayoutReachOptions /*< flags (glib-mkenums) >*/ +{ + LRO_NONE = (0 << 0), /* Aucune atteinte des bords */ + LRO_LEFT_TOP_REACH = (1 << 0), /* Atteinte du bord haut à G. */ + LRO_LEFT_BOTTOM_REACH = (1 << 1), /* Atteinte du bord bas à G. */ + LRO_RIGHT_TOP_REACH = (1 << 2), /* Atteinte du bord haut à D. */ + LRO_RIGHT_BOTTOM_REACH = (1 << 3), /* Atteinte du bord bas à D. */ + +} LayoutReachOptions; + +/* Met en place une disposition particulière de panneaux. */ +void apply_tiling_grid_layout(GtkGrid *, LayoutReachOptions, GtkWidget *[TGB_COUNT]); + + + + + + + +#if 0 + #include <gtk/gtk.h> @@ -81,5 +152,7 @@ void gtk_tiled_grid_restore_positions(const GtkTiledGrid *, GGenConfig *); void gtk_tiled_grid_save_positions(const GtkTiledGrid *, GGenConfig *); +#endif + -#endif /* _GTKEXT_TILEDGRID_H */ +#endif /* _GTKEXT_GRID_H */ diff --git a/src/gtkext/grid.ui b/src/gtkext/grid.ui new file mode 100644 index 0000000..b14ced2 --- /dev/null +++ b/src/gtkext/grid.ui @@ -0,0 +1,217 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="GtkTilingGrid" parent="GtkGrid"> + + <!-- Zone supérieure --> + + <child> + <object class="GtkRevealer" id="top"> + <property name="transition-type">slide-up</property> + <property name="reveal-child">false</property> + + <child> + <object class="GtkBox"> + <property name="orientation">vertical</property> + + <child> + <object class="GtkDockStation" id="top_station"> + <property name="orientation">vertical</property> + <property name="hexpand">true</property> + <property name="vexpand">false</property> + <property name="height-request">30</property> + <signal name="panel-docked" handler="gtk_tiling_grid_on_panel_un_docked"/> + <signal name="panel-undocked" handler="gtk_tiling_grid_on_panel_un_docked"/> + </object> + </child> + + <child> + <object class="GtkSeparator" id="top_handle"> + <property name="orientation">vertical</property> + <property name="height-request">5</property> + + <child> + <object class="GtkGestureDrag"> + <property name="propagation-phase">capture</property> + <signal name="drag-begin" handler="gtk_tiling_grid_on_gesture_drag_begin"/> + <signal name="drag-update" handler="gtk_tiling_grid_on_top_gesture_drag_update"/> + <signal name="drag-end" handler="gtk_tiling_grid_on_gesture_drag_end"/> + </object> + </child> + + </object> + </child> + + </object> + </child> + + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="column-span">3</property> + </layout> + </object> + </child> + + <!-- Zone de gauche --> + + <child> + <object class="GtkRevealer" id="left"> + <property name="transition-type">slide-right</property> + <property name="reveal-child">false</property> + + <child> + <object class="GtkBox"> + <property name="orientation">horizontal</property> + + <child> + <object class="GtkDockStation" id="left_station"> + <property name="orientation">vertical</property> + <property name="hexpand">false</property> + <property name="vexpand">true</property> + <property name="width-request">300</property> + <signal name="panel-docked" handler="gtk_tiling_grid_on_panel_un_docked"/> + <signal name="panel-undocked" handler="gtk_tiling_grid_on_panel_un_docked"/> + </object> + </child> + + <child> + <object class="GtkSeparator" id="left_handle"> + <property name="orientation">horizontal</property> + <property name="width-request">5</property> + + <child> + <object class="GtkGestureDrag"> + <property name="propagation-phase">capture</property> + <signal name="drag-begin" handler="gtk_tiling_grid_on_gesture_drag_begin"/> + <signal name="drag-update" handler="gtk_tiling_grid_on_left_gesture_drag_update"/> + <signal name="drag-end" handler="gtk_tiling_grid_on_gesture_drag_end"/> + </object> + </child> + + </object> + </child> + + </object> + </child> + + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + </object> + </child> + + <!-- Zone centrale --> + + <child> + <object class="GtkDockStation" id="main_station"> + <property name="hexpand">true</property> + <property name="vexpand">true</property> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + </object> + </child> + + <!-- Zone de droite --> + + <child> + <object class="GtkRevealer" id="right"> + <property name="transition-type">slide-left</property> + <property name="reveal-child">false</property> + + <child> + <object class="GtkBox"> + <property name="orientation">horizontal</property> + + <child> + <object class="GtkSeparator" id="right_handle"> + <property name="orientation">horizontal</property> + <property name="width-request">5</property> + + <child> + <object class="GtkGestureDrag"> + <property name="propagation-phase">capture</property> + <signal name="drag-begin" handler="gtk_tiling_grid_on_gesture_drag_begin"/> + <signal name="drag-update" handler="gtk_tiling_grid_on_right_gesture_drag_update"/> + <signal name="drag-end" handler="gtk_tiling_grid_on_gesture_drag_end"/> + </object> + </child> + + </object> + </child> + + <child> + <object class="GtkDockStation" id="right_station"> + <property name="orientation">vertical</property> + <property name="hexpand">false</property> + <property name="vexpand">true</property> + <property name="width-request">300</property> + <signal name="panel-docked" handler="gtk_tiling_grid_on_panel_un_docked"/> + <signal name="panel-undocked" handler="gtk_tiling_grid_on_panel_un_docked"/> + </object> + </child> + + </object> + </child> + + <layout> + <property name="column">2</property> + <property name="row">1</property> + </layout> + </object> + </child> + + <!-- Zone inférieure --> + + <child> + <object class="GtkRevealer" id="bottom"> + <property name="transition-type">slide-up</property> + <property name="reveal-child">false</property> + + <child> + <object class="GtkBox"> + <property name="orientation">vertical</property> + + <child> + <object class="GtkSeparator" id="bottom_handle"> + <property name="orientation">vertical</property> + <property name="height-request">5</property> + + <child> + <object class="GtkGestureDrag"> + <property name="propagation-phase">capture</property> + <signal name="drag-begin" handler="gtk_tiling_grid_on_gesture_drag_begin"/> + <signal name="drag-update" handler="gtk_tiling_grid_on_bottom_gesture_drag_update"/> + <signal name="drag-end" handler="gtk_tiling_grid_on_gesture_drag_end"/> + </object> + </child> + + </object> + </child> + + <child> + <object class="GtkDockStation" id="bottom_station"> + <property name="orientation">horizontal</property> + <property name="hexpand">true</property> + <property name="vexpand">false</property> + <property name="height-request">250</property> + <signal name="panel-docked" handler="gtk_tiling_grid_on_panel_un_docked"/> + <signal name="panel-undocked" handler="gtk_tiling_grid_on_panel_un_docked"/> + </object> + </child> + + </object> + </child> + + <layout> + <property name="column">0</property> + <property name="row">2</property> + <property name="column-span">3</property> + </layout> + </object> + </child> + + </template> +</interface> diff --git a/src/gtkext/gtkdockstation.c b/src/gtkext/gtkdockstation.c deleted file mode 100644 index 1757542..0000000 --- a/src/gtkext/gtkdockstation.c +++ /dev/null @@ -1,467 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * gtkdockstation.c - manipulation et l'affichage de composants rassemblés - * - * Copyright (C) 2012-2019 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "gtkdockstation.h" - - -#include <malloc.h> -#include <string.h> - - -#include "easygtk.h" -#include "../core/params.h" -#include "../common/extstr.h" -#include "../glibext/chrysamarshal.h" - - - -/* Procède à l'initialisation de l'afficheur concentré. */ -static void gtk_dock_station_class_init(GtkDockStationClass *); - -/* Procède à l'initialisation du support d'affichage concentré. */ -static void gtk_dock_station_init(GtkDockStation *); - -/* Met à jour le titre du support de panneaux concentrés. */ -static gboolean gtk_dock_station_switch_panel(GtkNotebook *, gpointer *, guint, GtkDockStation *); - - - - -/* Révèle ou cache la zone de recherches. */ -static void on_toggle_revealer(GtkToggleButton *, GtkDockStation *); - -/* Demande l'apparition d'un menu pour inclure des composants. */ -static void on_click_for_menu(GtkButton *, GtkDockStation *); - -/* Demande la disparition du composant courant. */ -static void on_click_for_close(GtkButton *, GtkDockStation *); - - - - - - -/* Détermine le type du composant d'affichage concentré. */ -G_DEFINE_TYPE(GtkDockStation, gtk_dock_station, GTK_TYPE_NOTEBOOK) - - -/****************************************************************************** -* * -* Paramètres : class = classe GTK à initialiser. * -* * -* Description : Procède à l'initialisation de l'afficheur concentré. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_dock_station_class_init(GtkDockStationClass *class) -{ - g_signal_new("dock-widget", - GTK_TYPE_DOCK_STATION, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GtkDockStationClass, dock_widget), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GTK_TYPE_WIDGET); - - g_signal_new("undock-widget", - GTK_TYPE_DOCK_STATION, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GtkDockStationClass, undock_widget), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GTK_TYPE_WIDGET); - - g_signal_new("switch-widget", - GTK_TYPE_DOCK_STATION, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GtkDockStationClass, switch_widget), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GTK_TYPE_WIDGET); - - g_signal_new("menu-requested", - GTK_TYPE_DOCK_STATION, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GtkDockStationClass, menu_requested), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GTK_TYPE_WIDGET); - - g_signal_new("close-requested", - GTK_TYPE_DOCK_STATION, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GtkDockStationClass, close_requested), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GTK_TYPE_WIDGET); - -} - - -/****************************************************************************** -* * -* Paramètres : station = composant GTK à initialiser. * -* * -* Description : Procède à l'initialisation du support d'affichage concentré. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_dock_station_init(GtkDockStation *station) -{ - GtkNotebook *notebook; /* Autre version du composant */ - GtkWidget *hbox; /* Division supérieure */ - GtkWidget *button; /* Bouton de contrôle */ - - notebook = GTK_NOTEBOOK(station); - - gtk_notebook_set_show_border(notebook, FALSE); - gtk_notebook_set_scrollable(notebook, TRUE); - - /* Définition de la zone de contrôle */ - - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_set_valign(hbox, GTK_ALIGN_CENTER); - gtk_widget_set_margin_end(hbox, 8); - gtk_widget_show(hbox); - - button = qck_create_toggle_button_with_named_img(G_OBJECT(station), "search", - "edit-find-symbolic", GTK_ICON_SIZE_MENU, NULL, - G_CALLBACK(on_toggle_revealer), station); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); - - button = qck_create_button_with_named_img(G_OBJECT(station), "menu", - "go-down-symbolic", GTK_ICON_SIZE_MENU, NULL, - G_CALLBACK(on_click_for_menu), station); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); - - button = qck_create_button_with_named_img(G_OBJECT(station), "close", - "window-close-symbolic", GTK_ICON_SIZE_MENU, NULL, - G_CALLBACK(on_click_for_close), station); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); - - gtk_notebook_set_action_widget(notebook, hbox, GTK_PACK_END); - - g_signal_connect(notebook, "switch-page", - G_CALLBACK(gtk_dock_station_switch_panel), station); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée un nouveau composant pour support d'affichage concentré.* -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -GtkWidget *gtk_dock_station_new(void) -{ - return g_object_new(GTK_TYPE_DOCK_STATION, NULL); - -} - - -/****************************************************************************** -* * -* Paramètres : notebook = support à l'origine de la mise à jour. * -* page = onglet mis en avant. * -* index = indice de l'onglet actuellement actif. * -* station = conteneur de gestion supérieur. * -* * -* Description : Met à jour le titre du support de panneaux concentrés. * -* * -* Retour : TRUE ? * -* * -* Remarques : - * -* * -******************************************************************************/ - -static gboolean gtk_dock_station_switch_panel(GtkNotebook *notebook, gpointer *page, guint index, GtkDockStation *station) -{ - GtkWidget *widget; /* Panneau concerné */ - GtkDockable *dockable; /* Elément encapsulé */ - GtkWidget *button; /* Bouton de contrôle */ - - widget = gtk_notebook_get_nth_page(notebook, index); - - dockable = GTK_DOCKABLE(g_object_get_data(G_OBJECT(widget), "dockable")); - - /* Mise à jour des boutons utilisables */ - - button = GTK_WIDGET(g_object_get_data(G_OBJECT(station), "search")); - - if (gtk_dockable_can_search(dockable)) - gtk_widget_show(button); - else - gtk_widget_hide(button); - - /* Remontée du changement d'onglet */ - - g_signal_emit_by_name(station, "switch-widget", widget); - - return TRUE; - -} - - -/****************************************************************************** -* * -* Paramètres : station = plateforme GTK à compléter. * -* dockable = nouvel élément à intégrer. * -* * -* Description : Ajoute un paquet d'informations à l'affichage centralisé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ -#include "gtkdisplaypanel.h" -#include "../gui/panels/history.h" -void gtk_dock_station_add_dockable(GtkDockStation *station, GtkDockable *dockable) -{ - GtkWidget *widget; /* Composant GTK à intégrer */ - char *name; /* Nom à donner à l'onglet */ - char *desc; /* Description à y associer */ - int max; /* Taille maximale des titres */ - GtkWidget *label; /* Etiquette d'onglet */ - GtkNotebook *notebook; /* Autre version du composant */ - - - - - - - - /* Récupération des éléments utiles */ - - widget = gtk_dockable_build_widget(dockable); - - //widget = gtk_button_new_with_label("123"); - gtk_widget_show(widget); - - - g_object_set_data(G_OBJECT(widget), "dockable", dockable); - - name = gtk_dockable_get_name(dockable); - desc = gtk_dockable_get_desc(dockable); - - /* Mise en place de la page */ - - if (!g_generic_config_get_value(get_main_configuration(), MPK_ELLIPSIS_TAB, &max)) - max = -1; - - name = ellipsis(name, max); - label = qck_create_label(NULL, NULL, name); - free(name); - - notebook = GTK_NOTEBOOK(station); - - if (gtk_notebook_get_n_pages(notebook) > 0) - g_signal_handlers_disconnect_by_func(notebook, - G_CALLBACK(gtk_dock_station_switch_panel), station); - - gtk_notebook_append_page(notebook, widget, label); - - gtk_widget_set_tooltip_text(label, desc); - - free(desc); - - if (gtk_notebook_get_n_pages(notebook) > 1) - g_signal_connect(notebook, "switch-page", - G_CALLBACK(gtk_dock_station_switch_panel), station); - - /* Lancement des mises à jour */ - - if (gtk_notebook_get_n_pages(notebook) > 1) - gtk_notebook_set_current_page(notebook, -1); - - //g_signal_emit_by_name(station, "dock-widget", widget); - -} - - -/****************************************************************************** -* * -* Paramètres : station = plateforme GTK à compléter. * -* widget = nouvel élément à intégrer. * -* * -* Description : Change le contenu de l'onglet courant uniquement. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_dock_panel_change_active_widget(GtkDockStation *station, GtkWidget *widget) -{ - GtkNotebook *notebook; /* Autre version du composant */ - gint index; /* Indice de l'onglet actif */ - GtkWidget *old; /* Ancien composant */ - GtkWidget *label; /* Etiquette d'onglet */ - char *str; /* Titre des prochaines fois */ - - notebook = GTK_NOTEBOOK(station); - - index = gtk_notebook_get_current_page(notebook); - - g_signal_handlers_disconnect_by_func(notebook, - G_CALLBACK(gtk_dock_station_switch_panel), station); - - old = gtk_notebook_get_nth_page(notebook, index); - label = gtk_notebook_get_tab_label(notebook, old); - - g_object_ref(G_OBJECT(label)); - str = g_object_get_data(G_OBJECT(old), "title"); - - gtk_notebook_remove_page(notebook, index); - gtk_notebook_insert_page(notebook, widget, label, index); - - g_object_unref(G_OBJECT(label)); - g_object_set_data(G_OBJECT(widget), "title", str); - - gtk_notebook_set_current_page(notebook, index); - - g_signal_connect(notebook, "switch-page", - G_CALLBACK(gtk_dock_station_switch_panel), station); - -} - - -/****************************************************************************** -* * -* Paramètres : station = plateforme GTK à compléter. * -* dockable = élément existant à retirer. * -* * -* Description : Retire un paquet d'informations de l'affichage centralisé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_dock_station_remove_dockable(GtkDockStation *station, GtkDockable *dockable) -{ - GtkNotebook *notebook; /* Autre version du composant */ - GtkWidget *widget; /* Composant GTK à retirer */ - gint index; /* Indice de l'onglet visé */ - - notebook = GTK_NOTEBOOK(station); - - widget = gtk_dockable_decompose(dockable, NULL); - - index = gtk_notebook_page_num(notebook, widget); - - gtk_notebook_remove_page(notebook, index); - -} - - -/****************************************************************************** -* * -* Paramètres : button = bouton à l'origine de la procédure. * -* station = station d'accueil pour différents composants. * -* * -* Description : Révèle ou cache la zone de recherches. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_toggle_revealer(GtkToggleButton *button, GtkDockStation *station) -{ - GtkNotebook *notebook; /* Autre version du composant */ - gint index; /* Indice de l'onglet courant */ - GtkWidget *widget; /* Panneau concerné */ - GtkDockable *dockable; /* Elément encapsulé */ - - notebook = GTK_NOTEBOOK(station); - - index = gtk_notebook_get_current_page(notebook); - widget = gtk_notebook_get_nth_page(notebook, index); - - dockable = GTK_DOCKABLE(g_object_get_data(G_OBJECT(widget), "dockable")); - - gtk_dockable_toggle_revealer(dockable, widget, gtk_toggle_button_get_active(button)); - -} - - -/****************************************************************************** -* * -* Paramètres : button = bouton à l'origine de la procédure. * -* station = station d'accueil pour différents composants. * -* * -* Description : Demande l'apparition d'un menu pour inclure des composants. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_click_for_menu(GtkButton *button, GtkDockStation *station) -{ - g_signal_emit_by_name(station, "menu-requested", button); - -} - - -/****************************************************************************** -* * -* Paramètres : button = bouton à l'origine de la procédure. * -* station = station d'accueil pour différents composants. * -* * -* Description : Demande la disparition du composant courant. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_click_for_close(GtkButton *button, GtkDockStation *station) -{ - g_signal_emit_by_name(station, "close-requested", button); - -} diff --git a/src/gtkext/gtkstatusstack.c b/src/gtkext/gtkstatusstack.c deleted file mode 100644 index fe4e4d5..0000000 --- a/src/gtkext/gtkstatusstack.c +++ /dev/null @@ -1,1121 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * gtkstatusstack.c - empilement d'informations de statut - * - * Copyright (C) 2015-2019 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "gtkstatusstack.h" - - -#include <assert.h> -#include <inttypes.h> -#include <malloc.h> -#include <string.h> - - -#include <i18n.h> - - -#include "easygtk.h" -#include "../gui/agroup.h" -#include "../format/format.h" - - - -/* ------------------------- GESTION EXTERIEURE DE LA BARRE ------------------------- */ - - -/* Navigation au sein d'assemblage */ -typedef struct _assembly_info assembly_info; - -/* Mémorisation des progressions */ -typedef struct _progress_info progress_info; - - -/* Abstration d'une gestion de barre de statut (instance) */ -struct _GtkStatusStack -{ - GtkBox parent; /* A laisser en premier */ - - GtkStack *main; /* Pile d'informations associée*/ - - GSourceFunc def_source; /* Appel en fin d'activité */ - - GObject *asm_ref; /* Espace de référencements #1 */ - assembly_info *asm_info; /* Informations courantes #1 */ - - GObject *prog_ref; /* Espace de référencements #2 */ - progress_info *prog_info; /* Informations courantes #2 */ - -}; - -/* Abstration d'une gestion de barre de statut (classe) */ -struct _GtkStatusStackClass -{ - GtkBoxClass parent; /* A laisser en premier */ - -}; - - -/* Initialise la classe des barres de statut améliorées. */ -static void gtk_status_stack_class_init(GtkStatusStackClass *); - -/* Initialise une instance de barre de statut améliorée. */ -static void gtk_status_stack_init(GtkStatusStack *); - -/* Supprime toutes les références externes. */ -static void gtk_status_stack_dispose(GtkStatusStack *); - -/* Procède à la libération totale de la mémoire. */ -static void gtk_status_stack_finalize(GtkStatusStack *); - - - -/* -------------------- STATUT DES INFORMATIONS DE DESASSEMBLAGE -------------------- */ - - -/* Navigation au sein d'assemblage */ -struct _assembly_info -{ - bool reset; /* Réinitialisation */ - - mrange_t current; /* Emplacement correspondant */ - - char *segment; /* Segment d'appartenance */ - - VMPA_BUFFER(phys); /* Localisation physique */ - VMPA_BUFFER(virt); /* Localisation virtuelle */ - - char *symbol; /* Eventuel symbole concerné */ - - const char *encoding; /* Encodage de l'instruction */ - phys_t size; /* Taille de l'instruction */ - -}; - - -/* Supprime l'empreinte mémoire d'informations d'assemblage. */ -static void reset_assembly_info(assembly_info *); - -/* Construit une barre d'état pour language d'assemblage. */ -static GtkWidget *build_assembly_status_stack(GtkStatusStack *); - -/* Réagit à un redimensionnement de la barre de désassemblage. */ -static void on_size_allocate_for_asm_status(GtkWidget *, GdkRectangle *, GObject *); - -/* Réagit à un clic sur l'icône de zoom. */ -static void on_zoom_icon_press(GtkEntry *, GtkEntryIconPosition, GdkEventButton *, GtkStatusStack *); - -/* S'assure de l'affichage à jour de la partie "assemblage". */ -static gboolean gtk_status_stack_show_current_location(GtkStatusStack *); - - - -/* -------------------------- STATUT DES SUIVIS D'ACTIVITE -------------------------- */ - - -/* Informations de progression */ -typedef struct _progress_status -{ - activity_id_t id; /* Identifiant unique */ - - char *message; /* Indication à faire valoir */ - - unsigned long current; /* Position courante */ - unsigned long max; /* Couverture à parcourir */ - - double last_updated; /* Dernière valeur poussée */ - -} progress_status; - -/* Mémorisation des progressions */ -struct _progress_info -{ - activity_id_t generator; /* Générateur de séquence */ - - progress_status *statuses; /* Statuts de progression */ - size_t count; /* Nombre de ces statuts */ - GMutex access; /* Accès à la pile */ - - guint tag; /* Identifiant de mise à jour */ - -}; - - -#define PROGRESS_SIZE 200 - - -/* Supprime l'empreinte mémoire d'informations d'activité. */ -static void reset_progress_info(progress_info *); - -/* Construit une barre d'état pour un suivi d'activité. */ -static GtkWidget *build_progress_status_stack(GtkStatusStack *); - -/* S'assure de l'affichage à jour de la partie "activité". */ -static gboolean gtk_status_stack_show_current_activity(GtkStatusStack *); - - - -/* ---------------------------------------------------------------------------------- */ -/* GESTION EXTERIEURE DE LA BARRE */ -/* ---------------------------------------------------------------------------------- */ - - -/* Détermine le type de la barre de statut améliorée. */ -G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX) - - -/****************************************************************************** -* * -* Paramètres : klass = classe GTK à initialiser. * -* * -* Description : Initialise la classe des barres de statut améliorées. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_status_stack_class_init(GtkStatusStackClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)gtk_status_stack_dispose; - object->finalize = (GObjectFinalizeFunc)gtk_status_stack_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : stack = instance GTK à initialiser. * -* * -* Description : Initialise une instance de barre de statut améliorée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_status_stack_init(GtkStatusStack *stack) -{ - GtkWidget *layer; /* Couche à empiler */ - - gtk_orientable_set_orientation(GTK_ORIENTABLE(stack), GTK_ORIENTATION_HORIZONTAL); - - stack->main = GTK_STACK(gtk_stack_new()); - gtk_widget_show(GTK_WIDGET(stack->main)); - gtk_box_pack_start(GTK_BOX(stack), GTK_WIDGET(stack->main), TRUE, TRUE, 8); - - stack->def_source = (GSourceFunc)gtk_status_stack_show_current_location; - - layer = build_assembly_status_stack(stack); - gtk_stack_add_named(stack->main, layer, "asm_info"); - - stack->asm_ref = G_OBJECT(layer); - stack->asm_info = (assembly_info *)calloc(1, sizeof(assembly_info)); - - reset_assembly_info(stack->asm_info); - - layer = build_progress_status_stack(stack); - gtk_stack_add_named(stack->main, layer, "prog_info"); - - stack->prog_ref = G_OBJECT(layer); - stack->prog_info = (progress_info *)calloc(1, sizeof(progress_info)); - - reset_progress_info(stack->prog_info); - - gtk_status_stack_reset_current_location(stack); - -} - - -/****************************************************************************** -* * -* Paramètres : view = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_status_stack_dispose(GtkStatusStack *stack) -{ - G_OBJECT_CLASS(gtk_status_stack_parent_class)->dispose(G_OBJECT(stack)); - -} - - -/****************************************************************************** -* * -* Paramètres : view = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_status_stack_finalize(GtkStatusStack *stack) -{ - reset_assembly_info(stack->asm_info); - free(stack->asm_info); - - reset_progress_info(stack->prog_info); - free(stack->prog_info); - - G_OBJECT_CLASS(gtk_status_stack_parent_class)->finalize(G_OBJECT(stack)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée une nouvelle instance de barre de statut. * -* * -* Retour : Composant GTK mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GtkStatusStack *gtk_status_stack_new(void) -{ - GtkStatusStack *result; /* Instance à retourner */ - - result = g_object_new(GTK_TYPE_STATUS_STACK, NULL); - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* STATUT DES INFORMATIONS DE DESASSEMBLAGE */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : info = informations à réinitialiser. * -* * -* Description : Supprime l'empreinte mémoire d'informations d'assemblage. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void reset_assembly_info(assembly_info *info) -{ - info->reset = true; - - if (info->segment != NULL) - { - free(info->segment); - info->segment = NULL; - } - - if (info->symbol != NULL) - { - free(info->symbol); - info->symbol = NULL; - } - -} - - -/****************************************************************************** -* * -* Paramètres : stack = composant global en cours de construction. * -* * -* Description : Construit une barre d'état pour language d'assemblage. * -* * -* Retour : Composant GTK mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GtkWidget *build_assembly_status_stack(GtkStatusStack *stack) -{ - GtkWidget *result; /* Support à retourner */ - GObject *ref; /* Espace de référencements */ - GtkWidget *hbox; /* Sous-division horizontale */ - GtkWidget *label; /* Etiquette pour impression */ - GtkWidget *zoom; /* Sélection du zoom courant */ - - result = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_show(result); - - ref = G_OBJECT(result); - - g_signal_connect(result, "size-allocate", G_CALLBACK(on_size_allocate_for_asm_status), ref); - - /* Première partie : navigation */ - - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 16); - gtk_widget_show(hbox); - gtk_box_pack_start(GTK_BOX(result), hbox, TRUE, TRUE, 8); - - label = qck_create_label(ref, "segment", NULL); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - - label = qck_create_label(ref, "phys", NULL); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - - label = qck_create_label(ref, "virt", NULL); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - - label = qck_create_label(ref, "offset", NULL); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - - /* Seconde partie : architecture */ - - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); - g_object_set_data(ref, "arch_box", hbox); - gtk_widget_show(hbox); - gtk_box_pack_start(GTK_BOX(result), hbox, FALSE, TRUE, 8); - - label = qck_create_label(ref, "arch", NULL); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - - label = qck_create_label(ref, "size", NULL); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - - /* Troisième partie : affichage */ - - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); - gtk_widget_show(hbox); - gtk_box_pack_start(GTK_BOX(result), hbox, FALSE, FALSE, 8); - - zoom = qck_create_entry(ref, "zoom", "100%"); - gtk_entry_set_icon_from_icon_name(GTK_ENTRY(zoom), GTK_ENTRY_ICON_SECONDARY, "go-up-symbolic"); - - g_signal_connect(zoom, "focus-in-event", G_CALLBACK(track_focus_change_in_text_area), NULL); - g_signal_connect(zoom, "focus-out-event", G_CALLBACK(track_focus_change_in_text_area), NULL); - g_signal_connect(zoom, "icon-press", G_CALLBACK(on_zoom_icon_press), stack); - - gtk_box_pack_start(GTK_BOX(hbox), zoom, FALSE, TRUE, 0); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : widget = composant graphique qui vient d'évoluer. * -* allocation = espace réservé pour le composant visé. * -* ref = espace de référencement global. * -* * -* Description : Réagit à un redimensionnement de la barre de désassemblage. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_size_allocate_for_asm_status(GtkWidget *widget, GdkRectangle *allocation, GObject *ref) -{ - GtkWidget *hbox; /* Sous-division horizontale */ - - hbox = GTK_WIDGET(g_object_get_data(ref, "arch_box")); - - gtk_widget_set_size_request(hbox, (allocation->width * 40) / 100, -1); - - /** - * On intervient après que le containeur soit passé collecter les tailles - * de ses enfants lors de son redimensionnement. - * - * Donc on force un prise en compte des changements. - */ - gtk_container_check_resize(GTK_CONTAINER(widget)); - -} - - -/****************************************************************************** -* * -* Paramètres : entry = zone de texte visée par la procédure. * -* icon_pos = position de l'image associée à l'entrée. * -* event = informations liées à l'événement. * -* stack = composant graphique de gestion des statuts. * -* * -* Description : Réagit à un clic sur l'icône de zoom. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_zoom_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEventButton *event, GtkStatusStack *stack) -{ - GtkWidget *popup; /* Popup à faire surgir */ - GdkRectangle rect; /* Zone précise à cibler */ - - if (event->button != GDK_BUTTON_PRIMARY) - return; - - popup = gtk_popover_new(GTK_WIDGET(entry)); - - gtk_entry_get_icon_area(entry, GTK_ENTRY_ICON_SECONDARY, &rect); - gtk_popover_set_pointing_to(GTK_POPOVER(popup), &rect); - - gtk_widget_show(popup); - -} - - -/****************************************************************************** -* * -* Paramètres : stack = barre de statut à actualiser. * -* range = emplacement à mettre en valeur. * -* segment = zone de binaire d'appartenance. * -* symbol = éventuelle position par rapport à un symbole. * -* encoding = encodage d'une éventuelle instruction ou NULL. * -* * -* Description : Actualise les informations liées une position d'assemblage. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_status_stack_update_current_location(GtkStatusStack *stack, const mrange_t *range, const char *segment, const char *symbol, const char *encoding) -{ - assembly_info *info; /* Informations à constituer */ - const vmpa2t *addr; /* Localisation de départ */ - phys_t size; /* Taille de l'emplacement */ - - info = stack->asm_info; - - /* Bascule vers une zone courante nouvelle ? */ - - addr = get_mrange_addr(range); - size = get_mrange_length(range); - - if (cmp_mrange(&info->current, range) == 0 - && info->size == size - && info->encoding == encoding) - goto useless; - - /* Réinitialisation */ - - reset_assembly_info(info); - - copy_mrange(&info->current, range); - - /* Zone d'appartenance */ - - info->segment = strdup(segment); - - /* Adresses de base */ - - vmpa2_phys_to_string(addr, MDS_UNDEFINED, info->phys, NULL); - - vmpa2_virt_to_string(addr, MDS_UNDEFINED, info->virt, NULL); - - info->encoding = encoding; - info->size = size; - - /* Symbole concerné */ - - if (symbol != NULL) - info->symbol = strdup(symbol); - - /* Nettoyage et conclusion */ - - info->reset = false; - - gtk_status_stack_show_current_location(stack); - - useless: - - ; - -} - - -/****************************************************************************** -* * -* Paramètres : stack = barre de statut à actualiser. * -* * -* Description : Réinitialise les informations associées une position. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_status_stack_reset_current_location(GtkStatusStack *stack) -{ - assembly_info *info; /* Informations à constituer */ - - info = stack->asm_info; - - reset_assembly_info(info); - - gtk_status_stack_show_current_location(stack); - -} - - -/****************************************************************************** -* * -* Paramètres : stack = pile de statuts à manipuler. * -* * -* Description : S'assure de l'affichage à jour de la partie "assemblage". * -* * -* Retour : G_SOURCE_REMOVE pour une exécution unique. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static gboolean gtk_status_stack_show_current_location(GtkStatusStack *stack) -{ - GObject *ref; /* Espace de référencements */ - assembly_info *info; /* Informations à consulter */ - GtkLabel *label; /* Etiquette à actualiser */ - char raw_pos[6 + VMPA_MAX_LEN + 1]; /* Formatage final en direct */ - char *content; /* Contenu dynamique */ - - stack->def_source = (GSourceFunc)gtk_status_stack_show_current_location; - - gtk_stack_set_visible_child_name(stack->main, "asm_info"); - - ref = stack->asm_ref; - info = stack->asm_info; - - /* Première partie : navigation */ - - if (info->reset) - { - label = GTK_LABEL(g_object_get_data(ref, "segment")); - gtk_label_set_text(label, NULL); - - label = GTK_LABEL(g_object_get_data(ref, "phys")); - gtk_label_set_text(label, NULL); - - label = GTK_LABEL(g_object_get_data(ref, "virt")); - gtk_label_set_text(label, NULL); - - label = GTK_LABEL(g_object_get_data(ref, "offset")); - gtk_label_set_text(label, NULL); - - } - else - { - label = GTK_LABEL(g_object_get_data(ref, "segment")); - gtk_label_set_text(label, info->segment); - - snprintf(raw_pos, sizeof(raw_pos), "phys: %s", info->phys); - - label = GTK_LABEL(g_object_get_data(ref, "phys")); - gtk_label_set_text(label, raw_pos); - - snprintf(raw_pos, sizeof(raw_pos), "virt: %s", info->virt); - - label = GTK_LABEL(g_object_get_data(ref, "virt")); - gtk_label_set_text(label, raw_pos); - - label = GTK_LABEL(g_object_get_data(ref, "offset")); - gtk_label_set_text(label, info->symbol != NULL ? info->symbol : ""); - - } - - /* Seconde partie : architecture */ - - if (info->reset || info->encoding == NULL || info->size == VMPA_NO_PHYSICAL) - { - label = GTK_LABEL(g_object_get_data(ref, "arch")); - gtk_label_set_text(label, NULL); - - label = GTK_LABEL(g_object_get_data(ref, "size")); - gtk_label_set_text(label, NULL); - - } - else - { - label = GTK_LABEL(g_object_get_data(ref, "arch")); - gtk_label_set_text(label, info->encoding); - - if (info->size > 1) - asprintf(&content, "%" PRIu64 " %s", (uint64_t)info->size, _("bytes")); - else - asprintf(&content, "%" PRIu64 " %s", (uint64_t)info->size, _("byte")); - - label = GTK_LABEL(g_object_get_data(ref, "size")); - gtk_label_set_text(label, content); - - free(content); - - } - - return G_SOURCE_REMOVE; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* STATUT DES SUIVIS D'ACTIVITE */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : info = informations à réinitialiser. * -* * -* Description : Supprime l'empreinte mémoire d'informations d'activité. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void reset_progress_info(progress_info *info) -{ - size_t i; /* Boucle de parcours */ - - if (info->tag != 0) - g_source_remove(info->tag); - - info->tag = 0; - - for (i = 0; i < info->count; i++) - { - if (info->statuses[i].message != NULL) - free(info->statuses[i].message); - } - - if (info->statuses != NULL) - { - free(info->statuses); - info->statuses = NULL; - } - - info->count = 0; - - g_mutex_init(&info->access); - -} - - -/****************************************************************************** -* * -* Paramètres : stack = composant global en cours de construction. * -* * -* Description : Construit une barre d'état pour un suivi d'activité. * -* * -* Retour : Composant GTK mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GtkWidget *build_progress_status_stack(GtkStatusStack *stack) -{ - GtkWidget *result; /* Support à retourner */ - GObject *ref; /* Espace de référencements */ - GtkWidget *progress; /* Barre de progression */ - GtkWidget *label; /* Désignation de l'activité */ - - result = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_show(result); - - ref = G_OBJECT(result); - - progress = gtk_progress_bar_new(); - g_object_set_data(ref, "progress", progress); - gtk_widget_set_size_request(progress, PROGRESS_SIZE, -1); - gtk_widget_set_valign(progress, GTK_ALIGN_CENTER); - gtk_widget_show(progress); - gtk_box_pack_start(GTK_BOX(result), progress, FALSE, TRUE, 8); - - label = qck_create_label(ref, "message", NULL); - gtk_box_pack_start(GTK_BOX(result), label, TRUE, TRUE, 0); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : stack = barre de statut à actualiser. * -* msg = nouveau message de statut à copier. * -* max = taille de la plage à parcourir. * -* * -* Description : Démarre le suivi d'une nouvelle activité. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *msg, unsigned long max) -{ - activity_id_t result; /* Numéro unique à renvoyer */ - progress_info *info; /* Informations à consulter */ - size_t new; /* Indice de l'activité créée */ - - if (stack == NULL) return NO_ACTIVITY_ID; - - info = stack->prog_info; - - g_mutex_lock(&info->access); - - result = ++info->generator; - - new = info->count++; - - info->statuses = (progress_status *)realloc(info->statuses, - info->count * sizeof(progress_status)); - - info->statuses[new].id = result; - - /* Intitulé */ - - if (msg == NULL) - info->statuses[new].message = NULL; - else - info->statuses[new].message = strdup(msg); - - /* Valeur */ - - info->statuses[new].current = 0; - info->statuses[new].max = max; - info->statuses[new].last_updated = 0; - - /* Actualisation */ - - if (info->tag != 0) - g_source_remove(info->tag); - - info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); - - g_mutex_unlock(&info->access); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : stack = barre de statut à actualiser. * -* id = identifiant de l'activité à cibler. * -* extra = nouvelle échéance supplémentaire des traitements. * -* * -* Description : Etend la portée des travaux d'une nouvelle activité. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_status_stack_extend_activity(GtkStatusStack *stack, activity_id_t id, unsigned long extra) -{ - progress_info *info; /* Informations à consulter */ - size_t i; /* Boucle de parcours */ - - if (stack == NULL) return; - - info = stack->prog_info; - - g_mutex_lock(&info->access); - - for (i = 0; i < info->count; i++) - if (info->statuses[i].id == id) - break; - - assert(i < info->count); - - info->statuses[i].max += extra; - - g_mutex_unlock(&info->access); - -} - - -/****************************************************************************** -* * -* Paramètres : stack = barre de statut à actualiser. * -* id = identifiant de l'activité à cibler. * -* msg = nouveau message de statut à copier. * -* * -* Description : Actualise les informations concernant une activité. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_status_stack_update_activity(GtkStatusStack *stack, activity_id_t id, const char *msg) -{ - progress_info *info; /* Informations à consulter */ - size_t i; /* Boucle de parcours */ - bool msg_changed; /* Changement d'intitulé */ - - if (stack == NULL) return; - - info = stack->prog_info; - - g_mutex_lock(&info->access); - - for (i = 0; i < info->count; i++) - if (info->statuses[i].id == id) - break; - - assert(i < info->count); - - /* Intitulé */ - - if (info->statuses[i].message != NULL) - { - if (msg == NULL) - msg_changed = true; - else - msg_changed = (strcmp(info->statuses[i].message, msg) != 0); - - free(info->statuses[i].message); - - } - else - msg_changed = (msg != NULL); - - if (msg == NULL) - info->statuses[i].message = NULL; - else - info->statuses[i].message = strdup(msg); - - /* On n'actualise que le sommet de la pile */ - - if ((i + 1) == info->count && msg_changed) - { - if (info->tag != 0) - g_source_remove(info->tag); - - info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); - - } - - g_mutex_unlock(&info->access); - -} - - -/****************************************************************************** -* * -* Paramètres : stack = barre de statut à actualiser. * -* id = identifiant de l'activité à cibler. * -* inc = nouvelle valeur pour une progression donnée. * -* * -* Description : Actualise la progression d'une activité. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t id, unsigned long inc) -{ - progress_info *info; /* Informations à consulter */ - size_t i; /* Boucle de parcours */ - progress_status *status; /* Raccourci de confort */ - double new; /* Nouvelle progression */ - - if (stack == NULL) return; - - info = stack->prog_info; - - g_mutex_lock(&info->access); - - for (i = 0; i < info->count; i++) - if (info->statuses[i].id == id) - break; - - assert(i < info->count); - - status = &info->statuses[i]; - - /* Valeur */ - - status->current += inc; - - new = (status->current * 1.0) / status->max; - - /* On n'actualise que le sommet de la pile */ - - if ((i + 1) == info->count && (new - status->last_updated) > (1.0 / PROGRESS_SIZE)) - { - status->last_updated = new; - - if (info->tag != 0) - g_source_remove(info->tag); - - info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); - - } - - g_mutex_unlock(&info->access); - -} - - -/****************************************************************************** -* * -* Paramètres : stack = barre de statut à actualiser. * -* * -* Description : Met fin au suivi d'une activité donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_status_stack_remove_activity(GtkStatusStack *stack, activity_id_t id) -{ - progress_info *info; /* Informations à consulter */ - size_t i; /* Boucle de parcours */ - - if (stack == NULL) return; - - info = stack->prog_info; - - g_mutex_lock(&info->access); - - for (i = 0; i < info->count; i++) - if (info->statuses[i].id == id) - break; - - assert(i < info->count); - - if (info->tag != 0) - g_source_remove(info->tag); - - if (info->statuses[i].message != NULL) - free(info->statuses[i].message); - - if (info->count == 1) - { - free(info->statuses); - info->statuses = NULL; - } - else - { - memmove(&info->statuses[i], &info->statuses[i + 1], - (info->count - i - 1) * sizeof(progress_status)); - - info->statuses = (progress_status *)realloc(info->statuses, - (info->count - 1) * sizeof(progress_status)); - - } - - info->count--; - - if (info->count == 0) - { - info->tag = 0; - g_idle_add(stack->def_source, stack); - } - else - info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); - - g_mutex_unlock(&info->access); - -} - - -/****************************************************************************** -* * -* Paramètres : stack = pile de statuts à manipuler. * -* * -* Description : S'assure de l'affichage à jour de la partie "activité". * -* * -* Retour : G_SOURCE_REMOVE pour une exécution unique. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static gboolean gtk_status_stack_show_current_activity(GtkStatusStack *stack) -{ - GObject *ref; /* Espace de référencements */ - progress_info *info; /* Informations à consulter */ - progress_status *last; /* Dernier statut à traiter */ - GtkProgressBar *progress; /* Barre de progression */ - GtkLabel *label; /* Désignation de l'activité */ - - if (!g_source_is_destroyed(g_main_current_source())) - { - gtk_stack_set_visible_child_name(stack->main, "prog_info"); - - ref = stack->prog_ref; - info = stack->prog_info; - - g_mutex_lock(&info->access); - - info->tag = 0; - - if (info->count > 0) - { - last = &info->statuses[info->count - 1]; - - progress = GTK_PROGRESS_BAR(g_object_get_data(ref, "progress")); - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), (last->current * 1.0) / last->max); - - label = GTK_LABEL(g_object_get_data(ref, "message")); - gtk_label_set_text(label, last->message); - - } - - g_mutex_unlock(&info->access); - - } - - return G_SOURCE_REMOVE; - -} diff --git a/src/gtkext/helpers.h b/src/gtkext/helpers.h new file mode 100644 index 0000000..3f8b3cd --- /dev/null +++ b/src/gtkext/helpers.h @@ -0,0 +1,43 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers.h - prototypes pour la simplification des interactions de base avec GTK + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GTKEXT_HELPERS_H +#define _GTKEXT_HELPERS_H + + +#include <assert.h> +#include <glib-object.h> + + + +/** + * Facilités de transmission de paramètres pour les fonctions de type + * gtk_widget_class_bind_template_callback_full() et gtk_builder_cscope_add_callback_symbol(). + */ + +#define BUILDER_CB(cb) \ + #cb, G_CALLBACK(cb) + + + +#endif /* _GTKEXT_HELPERS_H */ diff --git a/src/gtkext/hexview.c b/src/gtkext/hexview.c index 7363079..95b592e 100644 --- a/src/gtkext/hexview.c +++ b/src/gtkext/hexview.c @@ -37,17 +37,33 @@ /* ------------------------- BASES D'UN COMPOSANT GRAPHIQUE ------------------------- */ -/* Procède à l'initialisation de l'afficheur générique. */ +/* Liste des propriétés */ + +typedef enum _HexViewProperty { + + PROP_0, /* Réservé */ + + PROP_SHOW_OFFSETS, /* Affichage des positions */ + PROP_CONTENT, /* Contenu binaire affiché */ + + N_PROPERTIES + +} HexViewProperty; + +static GParamSpec *_hex_view_properties[N_PROPERTIES] = { NULL, }; + + +/* Initialise la classe des afficheurs de tampons bruts. */ static void gtk_hex_view_class_init(GtkHexViewClass *); -/* Procède à l'initialisation de l'afficheur générique. */ +/* Initialise une instance d'afficheur de tampons bruts. */ static void gtk_hex_view_init(GtkHexView *); /* Supprime toutes les références externes. */ -static void gtk_hex_view_dispose(GtkHexView *); +static void gtk_hex_view_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void gtk_hex_view_finalize(GtkHexView *); +static void gtk_hex_view_finalize(GObject *); /* Procède à l'actualisation de l'affichage d'un sous-composant. */ static void gtk_hex_view_dispatch_sub_snapshot(GtkWidget *, GtkSnapshot *, GtkWidget *); @@ -57,14 +73,14 @@ static void gtk_hex_view_populate_cache(GtkHexView *); -void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent); - - - +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ -/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ +/* Met à jour une propriété d'instance GObject. */ +static void gtk_hex_view_set_property(GObject *, guint, const GValue *, GParamSpec *); +/* Fournit la valeur d'une propriété d'instance GObject. */ +static void gtk_hex_view_get_property(GObject *, guint, GValue *, GParamSpec *); /* Prend acte de la taille allouée au composant d'affichage. */ static void gtk_hex_view_size_allocate(GtkWidget *, int, int, int); @@ -90,7 +106,7 @@ G_DEFINE_TYPE(GtkHexView, gtk_hex_view, GTK_TYPE_BUFFER_VIEW); * * * Paramètres : class = classe GTK à initialiser. * * * -* Description : Procède à l'initialisation de l'afficheur générique. * +* Description : Initialise la classe des afficheurs de tampons bruts. * * * * Retour : - * * * @@ -105,12 +121,26 @@ static void gtk_hex_view_class_init(GtkHexViewClass *class) object = G_OBJECT_CLASS(class); - object->dispose = (GObjectFinalizeFunc/* ! */)gtk_hex_view_dispose; - object->finalize = (GObjectFinalizeFunc)gtk_hex_view_finalize; + object->dispose = gtk_hex_view_dispose; + object->finalize = gtk_hex_view_finalize; + object->set_property = gtk_hex_view_set_property; + object->get_property = gtk_hex_view_get_property; + + _hex_view_properties[PROP_SHOW_OFFSETS] = + g_param_spec_boolean("show-offsets", NULL, NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + _hex_view_properties[PROP_CONTENT] = + g_param_spec_object("content", NULL, NULL, + G_TYPE_BIN_CONTENT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties(object, N_PROPERTIES, _hex_view_properties); widget = GTK_WIDGET_CLASS(class); - gtk_widget_class_set_css_name(widget, "GtkHexView"); + gtk_widget_class_set_css_name(widget, "hexview"); g_type_ensure(GTK_TYPE_COMPOSING_AREA); @@ -131,7 +161,7 @@ static void gtk_hex_view_class_init(GtkHexViewClass *class) * * * Paramètres : view = composant GTK à initialiser. * * * -* Description : Procède à l'initialisation de l'afficheur générique. * +* Description : Initialise une instance d'afficheur de tampons bruts. * * * * Retour : - * * * @@ -167,12 +197,14 @@ static void gtk_hex_view_init(GtkHexView *view) view->generator = NULL; + gtk_hex_view_create(view, NULL); + } /****************************************************************************** * * -* Paramètres : view = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -182,20 +214,24 @@ static void gtk_hex_view_init(GtkHexView *view) * * ******************************************************************************/ -static void gtk_hex_view_dispose(GtkHexView *view) +static void gtk_hex_view_dispose(GObject *object) { - gtk_widget_dispose_template(GTK_WIDGET(view), GTK_TYPE_HEX_VIEW); + GtkHexView *view; /* Version spécialisée */ + + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_HEX_VIEW); + + view = GTK_HEX_VIEW(object); g_clear_object(&view->generator); - G_OBJECT_CLASS(gtk_hex_view_parent_class)->dispose(G_OBJECT(view)); + G_OBJECT_CLASS(gtk_hex_view_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : view = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -205,9 +241,9 @@ static void gtk_hex_view_dispose(GtkHexView *view) * * ******************************************************************************/ -static void gtk_hex_view_finalize(GtkHexView *view) +static void gtk_hex_view_finalize(GObject *object) { - G_OBJECT_CLASS(gtk_hex_view_parent_class)->finalize(G_OBJECT(view)); + G_OBJECT_CLASS(gtk_hex_view_parent_class)->finalize(object); } @@ -263,13 +299,43 @@ bool gtk_hex_view_create(GtkHexView *view, GBinContent *content) parent = GTK_BUFFER_VIEW(view); - cache = g_buffer_cache_new(1, 2); + cache = g_buffer_cache_new(1 /* opt_count */, 2 /* reg_count */); parent->view = g_buffer_view_new(cache, parent->style); unref_object(cache); - view->generator = g_hex_generator_new(content); + + + gtk_hex_view_set_content(view, content); + + + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : view = composant d'affichage à consulter. * +* * +* Description : Fournit le contenu associé au composant d'affichage. * +* * +* Retour : Contenu dans lequel puise le générateur pour les lignes. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBinContent *gtk_hex_view_get_content(const GtkHexView *view) +{ + GBinContent *result; /* Référence à retourner */ + + if (view->generator != NULL) + result = g_hex_generator_get_content(view->generator); + else + result = NULL; return result; @@ -278,6 +344,61 @@ bool gtk_hex_view_create(GtkHexView *view, GBinContent *content) /****************************************************************************** * * +* Paramètres : view = composant d'affichage à modifier. * +* content = nouveau contenu pour source de génération. * +* * +* Description : Définit le contenu associé au composant d'affichage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_hex_view_set_content(GtkHexView *view, GBinContent *content) +{ + GBinContent *old; /* Ancienne valeur */ + GBufferCache *cache; /* Tampon à représenter */ + + old = gtk_hex_view_get_content(view); + + assert((old == NULL && view->generator == NULL) || (old != NULL && view->generator != NULL)); + + if (old != content) + { + if (view->generator != NULL) + { + cache = g_buffer_view_get_cache(GTK_BUFFER_VIEW(view)->view); + + g_buffer_cache_wlock(cache); + + g_buffer_cache_truncate(cache, 0); + + g_buffer_cache_wunlock(cache); + + unref_object(cache); + + g_clear_object(&view->generator); + + } + + if (content != NULL) + view->generator = g_hex_generator_new(content); + + g_object_notify_by_pspec(G_OBJECT(view), _hex_view_properties[PROP_CONTENT]); + + assert(content != NULL); + gtk_widget_queue_resize(GTK_WIDGET(view)); + + } + + g_clear_object(&old); + +} + + +/****************************************************************************** +* * * Paramètres : widget = composant GTK à redessiner. * * snapshot = gestionnaire de noeuds de rendu à solliciter. * * parent = composant GTK parent et cadre de l'appel. * @@ -380,67 +501,108 @@ static void gtk_hex_view_populate_cache(GtkHexView *view) /* Mise à jour de l'affichage ? */ + /* if (needed != count) gtk_widget_queue_resize(GTK_WIDGET(view)); + */ } +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à prendre en compte. * +* pspec = définition de la propriété. * +* * +* Description : Met à jour une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - -void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent) +static void gtk_hex_view_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - GdkRGBA red, green, yellow, blue; - float w, h; - - gdk_rgba_parse (&red, "red"); - gdk_rgba_parse (&green, "green"); - gdk_rgba_parse (&yellow, "yellow"); - gdk_rgba_parse (&blue, "blue"); + GtkHexView *view; /* Version spécialisée */ + GObject *content; /* Contenu sous forme simple */ - w = gtk_widget_get_width (widget) / 2.0; - h = gtk_widget_get_height (widget) / 2.0; + view = GTK_HEX_VIEW(object); - h /= 2.0; + switch (prop_id) + { + case PROP_SHOW_OFFSETS: + g_display_options_set(GTK_CONTENT_VIEW(view)->options, HCO_OFFSET, g_value_get_boolean(value)); + gtk_widget_set_visible(view->offsets, g_value_get_boolean(value)); + break; - gtk_snapshot_append_color (snapshot, &red, - &GRAPHENE_RECT_INIT(0, 0, w, h)); - gtk_snapshot_append_color (snapshot, &green, - &GRAPHENE_RECT_INIT(w, 0, w, h)); - gtk_snapshot_append_color (snapshot, &yellow, - &GRAPHENE_RECT_INIT(0, h, w, h)); - gtk_snapshot_append_color (snapshot, &blue, - &GRAPHENE_RECT_INIT(w, h, w, h)); + case PROP_CONTENT: + content = g_value_get_object(value); + gtk_hex_view_set_content(view, G_BIN_CONTENT(content)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } } +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à transmettre. [OUT] * +* pspec = définition de la propriété. * +* * +* Description : Fournit la valeur d'une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ +static void gtk_hex_view_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GtkHexView *view; /* Version spécialisée */ + view = GTK_HEX_VIEW(object); + switch (prop_id) + { + case PROP_SHOW_OFFSETS: + g_value_set_boolean(value, gtk_widget_get_visible(view->offsets)); + break; + case PROP_CONTENT: + g_value_take_object(value, gtk_hex_view_get_content(view)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } - - - - -/* ---------------------------------------------------------------------------------- */ -/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ -/* ---------------------------------------------------------------------------------- */ +} /****************************************************************************** * * -* Paramètres : widget = composant GTK à examiner. * -* width = largeur affectée au composant graphique. * -* height = hauteur affectée au composant graphique. * +* Paramètres : widget = composant GTK à examiner. * +* width = largeur affectée au composant graphique. * +* height = hauteur affectée au composant graphique. * * baseline = ligne de base affectée au composant graphique. * * * * Description : Prend acte de la taille allouée au composant d'affichage. * @@ -467,7 +629,13 @@ static void gtk_hex_view_size_allocate(GtkWidget *widget, int width, int height, final_widths = alloca(_CHILDREN_COUNT * sizeof(int)); for (i = 0; i < _CHILDREN_COUNT; i++) - gtk_widget_measure(view->children[i], GTK_ORIENTATION_HORIZONTAL, -1, &min_widths[i], NULL, NULL, NULL); + { + if (!gtk_widget_get_visible(view->children[i])) + min_widths[i] = 0; + else + gtk_widget_measure(view->children[i], GTK_ORIENTATION_HORIZONTAL, -1, &min_widths[i], NULL, NULL, NULL); + + } /* Passe 1 : tentative sans défilement vertical */ @@ -491,7 +659,10 @@ static void gtk_hex_view_size_allocate(GtkWidget *widget, int width, int height, for (i = 0; i < _CHILDREN_COUNT; i++) { - final_widths[i] += min_widths[i]; + if (!gtk_widget_get_visible(view->children[i])) + final_widths[i] = 0; + else + final_widths[i] += min_widths[i]; g_width_tracker_set_column_min_width(tracker, i, final_widths[i]); @@ -531,17 +702,6 @@ static GtkSizeRequestMode gtk_hex_view_get_request_mode(GtkWidget *widget) } - - - - - - - - - - - /****************************************************************************** * * * Paramètres : widget = composant GTK à examiner. * @@ -580,8 +740,12 @@ static void gtk_hex_view_measure(GtkWidget *widget, GtkOrientation orientation, for (i = 0; i < _CHILDREN_COUNT; i++) { + if (!gtk_widget_get_visible(view->children[i])) + continue; + gtk_widget_measure(view->children[i], GTK_ORIENTATION_HORIZONTAL, -1, &min, NULL, NULL, NULL); requested += min; + } for_size -= requested; @@ -593,11 +757,14 @@ static void gtk_hex_view_measure(GtkWidget *widget, GtkOrientation orientation, GTK_BUFFER_VIEW(view)->style, for_size); - if (minimum != NULL) *minimum = 0; + if (minimum != NULL) *minimum = requested; if (natural != NULL) *natural = requested; for (i = 0; i < _CHILDREN_COUNT; i++) { + if (!gtk_widget_get_visible(view->children[i])) + continue; + gtk_widget_measure(view->children[i], GTK_ORIENTATION_VERTICAL, -1, &min, &nat, NULL, NULL); if (minimum != NULL && min > *minimum) diff --git a/src/gtkext/hexview.h b/src/gtkext/hexview.h index 2199786..0d2cd5a 100644 --- a/src/gtkext/hexview.h +++ b/src/gtkext/hexview.h @@ -41,6 +41,12 @@ DECLARE_GTYPE(GtkHexView, gtk_hex_view, GTK, HEX_VIEW); /* Crée un composant d'affichage d'octets bruts et imprimables. */ GtkHexView *gtk_hex_view_new(GBinContent *); +/* Fournit le contenu associé au composant d'affichage. */ +GBinContent *gtk_hex_view_get_content(const GtkHexView *); + +/* Définit le contenu associé au composant d'affichage. */ +void gtk_hex_view_set_content(GtkHexView *, GBinContent *); + #endif /* _GTKEXT_HEXVIEW_H */ diff --git a/src/gtkext/hexview.ui b/src/gtkext/hexview.ui index ae4586c..9b42936 100644 --- a/src/gtkext/hexview.ui +++ b/src/gtkext/hexview.ui @@ -1,27 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> <interface> - <template class="GtkHexView" parent="GtkBufferView"> - <property name="css-name">GtkHexView</property> - <child> - <object class="GtkComposingArea" id="offsets"> - <style> - <class name="gutter"/> - </style> - </object> - </child> - <child> - <object class="GtkComposingArea" id="hex"> - <property name="hexpand">true</property> - <style> - <class name="custom-view"/> - </style> - </object> - </child> - <child> - <object class="GtkComposingArea" id="ascii"> - <style> - <class name="custom-view"/> - </style> - </object> - </child> - </template> + + <template class="GtkHexView" parent="GtkBufferView"> + <child> + <object class="GtkComposingArea" id="offsets"> + <style> + <class name="gutter"/> + </style> + </object> + </child> + <child> + <object class="GtkComposingArea" id="hex"> + <property name="hexpand">true</property> + <style> + <class name="custom-view"/> + </style> + </object> + </child> + <child> + <object class="GtkComposingArea" id="ascii"> + <style> + <class name="custom-view"/> + </style> + </object> + </child> + </template> + </interface> diff --git a/src/gtkext/launcher-int.h b/src/gtkext/launcher-int.h new file mode 100644 index 0000000..07152f0 --- /dev/null +++ b/src/gtkext/launcher-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweak-int.h - définitions internes pour un lanceur de panneau majeur + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GTKEXT_LAUNCHER_INT_H +#define _GTKEXT_LAUNCHER_INT_H + + +#include "launcher.h" + + + +/* Elément de lancement d'un panneau majeur pour l'interface (instance) */ +struct _GtkPanelLauncher +{ + GtkListBoxRow parent; /* A laisser en premier */ + + GtkImage *icon; /* Eventuelle image */ + GtkLabel *title; /* Etiquette associée */ + GtkLabel *desc; /* Description du panneau */ + +}; + +/* Elément de lancement d'un panneau majeur pour l'interface (classe) */ +struct _GtkPanelLauncherClass +{ + GtkListBoxRowClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un nouveau lanceur de panneau majeur. */ +bool gtk_panel_launcher_create(GtkPanelLauncher *, const char *, const char *, const char *); + + + +#endif /* _GTKEXT_LAUNCHER_INT_H */ diff --git a/src/gtkext/launcher.c b/src/gtkext/launcher.c new file mode 100644 index 0000000..5bb850b --- /dev/null +++ b/src/gtkext/launcher.c @@ -0,0 +1,211 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * launcher.c - lanceur de panneau majeur + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "launcher.h" + + +#include <malloc.h> +#include <string.h> + + +#include "helpers.h" +#include "launcher-int.h" +#include "../common/extstr.h" + + + +/* Initialise la classe des sections d'éléments paramétrables. */ +static void gtk_panel_launcher_class_init(GtkPanelLauncherClass *); + +/* Initialise une instance de lanceur de panneau majeur. */ +static void gtk_panel_launcher_init(GtkPanelLauncher *); + +/* Supprime toutes les références externes. */ +static void gtk_panel_launcher_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_panel_launcher_finalize(GObject *); + + + +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkPanelLauncher, gtk_panel_launcher, GTK_TYPE_LIST_BOX_ROW); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des sections d'éléments paramétrables. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_panel_launcher_class_init(GtkPanelLauncherClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_panel_launcher_dispose; + object->finalize = gtk_panel_launcher_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/launcher.ui"); + + gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, icon); + gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, title); + gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, desc); + +} + + +/****************************************************************************** +* * +* Paramètres : launcher = composant GTK à initialiser. * +* * +* Description : Initialise une instance de lanceur de panneau majeur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_panel_launcher_init(GtkPanelLauncher *launcher) +{ + gtk_widget_init_template(GTK_WIDGET(launcher)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_panel_launcher_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PANEL_LAUNCHER); + + G_OBJECT_CLASS(gtk_panel_launcher_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_panel_launcher_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_panel_launcher_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : icon = désignation de l'image de représentation. * +* title = titre principal à afficher. * +* desc = description du panneau ciblé. * +* * +* Description : Crée un nouveau lanceur de panneau majeur. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkPanelLauncher *gtk_panel_launcher_new(const char *icon, const char *title, const char *desc) +{ + GtkPanelLauncher *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_PANEL_LAUNCHER, NULL); + + if (!gtk_panel_launcher_create(result, icon, title, desc)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : launcher = lanceur à initialiser pleinement. * +* icon = désignation de l'image de représentation. * +* title = titre principal à afficher. * +* desc = description du panneau ciblé. * +* * +* Description : Met en place un nouveau lanceur de panneau majeur. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_panel_launcher_create(GtkPanelLauncher *launcher, const char *icon, const char *title, const char *desc) +{ + bool result; /* Bilan à retourner */ + char *bold; /* Titre sublimé */ + + result = true; + + gtk_image_set_from_icon_name(launcher->icon, icon); + + bold = strdup(title); + bold = strprep(bold, "<b>"); + bold = stradd(bold, "</b>"); + + gtk_label_set_label(launcher->title, bold); + + free(bold); + + gtk_label_set_label(launcher->desc, desc); + + return result; + +} diff --git a/src/gtkext/launcher.h b/src/gtkext/launcher.h new file mode 100644 index 0000000..3857216 --- /dev/null +++ b/src/gtkext/launcher.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * launcher.h - prototypes pour pour un lanceur de panneau majeur + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GTKEXT_LAUNCHER_H +#define _GTKEXT_LAUNCHER_H + + +#include <gtk/gtk.h> + + +#include "../glibext/helpers.h" + + + +#define GTK_TYPE_PANEL_LAUNCHER (gtk_panel_launcher_get_type()) + +DECLARE_GTYPE(GtkPanelLauncher, gtk_panel_launcher, GTK, PANEL_LAUNCHER); + + +/* Crée un nouveau lanceur de panneau majeur. */ +GtkPanelLauncher *gtk_panel_launcher_new(const char *, const char *, const char *); + + + +#endif /* _GTKEXT_LAUNCHER_H */ diff --git a/src/gtkext/launcher.ui b/src/gtkext/launcher.ui new file mode 100644 index 0000000..f6f4fec --- /dev/null +++ b/src/gtkext/launcher.ui @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="GtkPanelLauncher" parent="GtkListBoxRow"> + + <property name="child"> + + <object class="GtkGrid"> + <property name="margin-bottom">12</property> + <property name="margin-end">12</property> + <property name="margin-start">12</property> + <property name="margin-top">12</property> + <property name="column-spacing">12</property> + + <child> + <object class="GtkImage" id="icon"> + <property name="icon-name"></property> + <property name="pixel-size">48</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="row-span">2</property> + </layout> + <style> + <class name="icon-dropshadow"/> + </style> + </object> + </child> + + <child> + <object class="GtkLabel" id="title"> + <property name="label"></property> + <property name="use-markup">TRUE</property> + <property name="xalign">0</property> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> + </object> + </child> + + <child> + <object class="GtkLabel" id="desc"> + <property name="label"></property> + <property name="hexpand">true</property> + <property name="xalign">0</property> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + <style> + <class name="dim-label"/> + </style> + </object> + </child> + + <child> + <object class="GtkImage"> + <property name="icon-name">go-next-symbolic</property> + <property name="margin-start">12</property> + <layout> + <property name="column">2</property> + <property name="row">0</property> + <property name="row-span">2</property> + </layout> + <style> + <class name="icon-dropshadow"/> + </style> + </object> + </child> + + </object> + + </property> + + </template> +</interface> diff --git a/src/gui/panel-int.h b/src/gtkext/panel-int.h index d54dc16..5398e51 100644 --- a/src/gui/panel-int.h +++ b/src/gtkext/panel-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * panel-int.h - prototypes pour les définitions internes liées aux panneaux d'affichage * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,13 +22,52 @@ */ -#ifndef _GUI_PANELS_PANEL_INT_H -#define _GUI_PANELS_PANEL_INT_H +#ifndef _GTKEXT_PANEL_INT_H +#define _GTKEXT_PANEL_INT_H #include "panel.h" + +/* Indique l'emplacement par défaut pour un affichage. */ +typedef char * (* get_tiled_panel_path) (const GtkTiledPanel *); + +/* Fournit les composants adaptés pour la barre de titre. */ +typedef GListStore * (* get_tiled_panel_widgets_cb) (const GtkTiledPanel *, bool); + +/* Note un ajout ou un retrait de panneau principal. */ +typedef void (* notify_tiled_panel_state_cb) (GtkTiledPanel *, GtkTiledPanel *, bool); + + +/* Elément réactif pour panneaux de l'éditeur (instance) */ +struct _GtkTiledPanel +{ + GtkBox parent; /* A laisser en premier */ + +}; + +/* Elément réactif pour panneaux de l'éditeur (classe) */ +struct _GtkTiledPanelClass +{ + GtkBoxClass parent; /* A laisser en premier */ + + get_tiled_panel_path get_default_path; /* Localisation de l'affichage */ + get_tiled_panel_widgets_cb get_widgets; /* Récupération de composants */ + + notify_tiled_panel_state_cb notify; /* Note d'un ajout ou retrait */ + +}; + + + + + + + + +#if 0 + #include <gtk/gtk.h> @@ -134,5 +173,8 @@ void g_panel_item_switch_to_updating_mask(GPanelItem *); void g_panel_item_switch_to_updated_content(GPanelItem *); +#endif + + -#endif /* _GUI_PANELS_PANEL_INT_H */ +#endif /* _GTKEXT_PANEL_INT_H */ diff --git a/src/gui/panel.c b/src/gtkext/panel.c index 5b21620..f63cfa1 100644 --- a/src/gui/panel.c +++ b/src/gtkext/panel.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * panel.c - gestion des éléments réactifs spécifiques aux panneaux * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,6 +25,206 @@ #include "panel.h" +#include "panel-int.h" + + + + + +/* Initialise la classe des panneaux graphiques de l'éditeur. */ +static void gtk_tiled_panel_class_init(GtkTiledPanelClass *); + +/* Initialise une instance de panneau graphique pour l'éditeur. */ +static void gtk_tiled_panel_init(GtkTiledPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_tiled_panel_dispose(GtkTiledPanel *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_tiled_panel_finalize(GtkTiledPanel *); + + + + + + + + +/* Détermine le type du conteneur d'affichage en tuiles nommées. */ +G_DEFINE_TYPE(GtkTiledPanel, gtk_tiled_panel, GTK_TYPE_BOX) + + +/****************************************************************************** +* * +* Paramètres : klass = classe GTK à initialiser. * +* * +* Description : Initialise la classe des panneaux graphiques de l'éditeur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiled_panel_class_init(GtkTiledPanelClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)gtk_tiled_panel_dispose; + object->finalize = (GObjectFinalizeFunc)gtk_tiled_panel_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance GTK à initialiser. * +* * +* Description : Initialise une instance de panneau graphique pour l'éditeur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiled_panel_init(GtkTiledPanel *panel) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiled_panel_dispose(GtkTiledPanel *panel) +{ + G_OBJECT_CLASS(gtk_tiled_panel_parent_class)->dispose(G_OBJECT(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tiled_panel_finalize(GtkTiledPanel *panel) +{ + G_OBJECT_CLASS(gtk_tiled_panel_parent_class)->finalize(G_OBJECT(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau graphique à consulter. * +* * +* Description : Indique l'emplacement attendu pour un affichage. * +* * +* Retour : Chemin représenté ou NULL pour l'emplacement "M" par défaut. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *gtk_tiled_panel_get_path(const GtkTiledPanel *panel) +{ + char *result; /* Chemin à retourner */ + GtkTiledPanelClass *class; /* Classe à actionner */ + + class = GTK_TILED_PANEL_GET_CLASS(panel); + + if (class->get_default_path != NULL) + result = class->get_default_path(panel); + else + result = NULL; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau graphique à consulter. * +* left = indication quant au côté ciblé. * +* * +* Description : Fournit les composants adaptés pour la barre de titre. * +* * +* Retour : Liste de Composants GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GListStore *gtk_tiled_panel_get_title_widgets(const GtkTiledPanel *panel, bool left) +{ + GListStore *result; /* Composant(s) à retourner */ + GtkTiledPanelClass *class; /* Classe à actionner */ + + class = GTK_TILED_PANEL_GET_CLASS(panel); + + if (class->get_widgets != NULL) + result = class->get_widgets(panel, left); + else + result = NULL; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau graphique à manipuler. * +* main = panneau principal visé par l'opération. * +* activated = nature du changement de statut : ajout, retrait ?* +* * +* Description : Note un ajout ou un retrait de panneau principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_tiled_panel_notify_new_main_panel_state(GtkTiledPanel *panel, GtkTiledPanel *main, bool activated) +{ + GtkTiledPanelClass *class; /* Classe à actionner */ + + class = GTK_TILED_PANEL_GET_CLASS(panel); + + if (class->notify != NULL) + class->notify(panel, main, activated); + +} + + + + + + +#if 0 + #include <assert.h> #include <stdio.h> #include <string.h> @@ -859,7 +1059,7 @@ void g_panel_item_set_dock_at_startup(GPanelItem *item, bool status) bool g_panel_item_is_docked(const GPanelItem *item) { - bool result; /* Status à retourner */ + bool result; /* Statut à retourner */ result = item->docked; @@ -1117,3 +1317,5 @@ void g_panel_item_switch_to_updated_content(GPanelItem *item) g_atomic_int_dec_and_test(&item->switched); } + +#endif diff --git a/src/gui/panel.h b/src/gtkext/panel.h index de8d2bf..9b00657 100644 --- a/src/gui/panel.h +++ b/src/gtkext/panel.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * panel.h - prototypes pour la gestion des éléments réactifs spécifiques aux panneaux * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,10 +22,38 @@ */ -#ifndef _GUI_PANELS_PANEL_H -#define _GUI_PANELS_PANEL_H +#ifndef _GTKEXT_PANEL_H +#define _GTKEXT_PANEL_H +#include <gtk/gtk.h> + + +#include "../glibext/helpers.h" + + + +#define GTK_TYPE_TILED_PANEL (gtk_tiled_panel_get_type()) + +DECLARE_GTYPE(GtkTiledPanel, gtk_tiled_panel, GTK, TILED_PANEL); + + +/* Indique l'emplacement attendu pour un affichage. */ +char *gtk_tiled_panel_get_path(const GtkTiledPanel *); + +/* Fournit les composants adaptés pour la barre de titre. */ +GListStore *gtk_tiled_panel_get_title_widgets(const GtkTiledPanel *, bool); + +/* Note un ajout ou un retrait de panneau principal. */ +void gtk_tiled_panel_notify_new_main_panel_state(GtkTiledPanel *, GtkTiledPanel *, bool); + + + + + + +#if 0 + #include <stdbool.h> #include <gtk/gtk.h> @@ -108,5 +136,7 @@ bool g_panel_item_is_docked(const GPanelItem *); void g_panel_item_undock(GPanelItem *); +#endif + -#endif /* _GUI_PANELS_PANEL_H */ +#endif /* _GTKEXT_PANEL_H */ diff --git a/src/gtkext/statusstack-int.h b/src/gtkext/statusstack-int.h new file mode 100644 index 0000000..721b982 --- /dev/null +++ b/src/gtkext/statusstack-int.h @@ -0,0 +1,106 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * statusstack-int.h - définitions internes pour l'empilement d'informations de statut + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GTKEXT_STATUSSTACK_INT_H +#define _GTKEXT_STATUSSTACK_INT_H + + +#include "statusstack.h" +#include "../glibext/secstorage.h" + + + +/* Navigation au sein d'assemblage */ +typedef struct _navigation_info_t navigation_info_t; + +/* Mémorisation des progressions au sein d'activités */ +typedef struct _activity_info_t activity_info_t; + + +#define NETWORK_UPDATE_COUNT 10 +#define NETWORK_UPDATE_INTERVAL (1000 / NETWORK_UPDATE_COUNT) + + +/* Gestion de barre de statut adaptable (instance) */ +struct _GtkStatusStack +{ + GtkBox parent; /* A laisser en premier */ + + GtkStack *main; /* Pile d'informations associée*/ + + GSourceFunc def_source; /* Appel en fin d'activité */ + + /* Message simple par défaut */ + + GtkLabel *def_label; /* Afficheur de message */ + + char *msg; /* Contenu associé */ + + /* Navigation */ + + GtkLabel *nav_segment; /* Désignation du segment */ + GtkLabel *nav_phys; /* Adresse physique */ + GtkLabel *nav_virt; /* Adresse virtuelle */ + GtkLabel *nav_offset; /* Position dans le binaire */ + GtkLabel *nav_format; /* Architecture du binaire */ + GtkLabel *nav_details; /* Détails sur l'architecture */ + GtkEntry *zoom; /* Degré de zoom courant */ + + navigation_info_t *nav_info; /* Informations brutes liées */ + + /* Activité */ + + GtkLabel *activity_message; /* Nature de l'activité */ + GtkProgressBar *activity_progress; /* Barre de progression */ + + activity_info_t *activity_info; /* Informations brutes liées */ + + /* Tronc commun */ + + GSecretStorage *storage; /* Stockage des secrets */ + GtkToggleButton *lock_update; /* Activation des accès */ + + GtkLabel *net_recv_speed; /* Débit en réception */ + GtkLabel *net_send_speed; /* Débit en émission */ + + size_t last_bytes_received[NETWORK_UPDATE_COUNT]; /* Octets reçus */ + size_t last_bytes_sent[NETWORK_UPDATE_COUNT]; /* Octets émis */ + gint64 last_timestamps[NETWORK_UPDATE_COUNT]; /* Dates des mesures */ + size_t next_index; /* Indice d'écriture */ + + guint network_update_tag; /* Identifiant de mise à jour */ + + GtkToggleButton *bottom_toggler; /* Bascule de panneaux inf. */ + +}; + +/* Gestion de barre de statut adaptable (classe) */ +struct _GtkStatusStackClass +{ + GtkBoxClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GTKEXT_STATUSSTACK_INT_H */ diff --git a/src/gtkext/statusstack.c b/src/gtkext/statusstack.c new file mode 100644 index 0000000..92c296a --- /dev/null +++ b/src/gtkext/statusstack.c @@ -0,0 +1,1383 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * statusstack.c - empilement d'informations de statut + * + * Copyright (C) 2015-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "statusstack.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include <i18n.h> + + +#include "helpers.h" +#include "statusstack-int.h" +#include "../core/global.h" + + + +/* -------------------------- GESTION GENERALE DES STATUTS -------------------------- */ + + +/* Liste des propriétés */ + +typedef enum _StatusStackProperty { + + PROP_0, /* Réservé */ + + PROP_SHOW_BOTTOM, /* Affichage de la zone inf. */ + + N_PROPERTIES + +} StatusStackProperty; + +static GParamSpec *_status_stack_properties[N_PROPERTIES] = { NULL, }; + + +/* Source d'affichage par défaut */ +#define gtk_status_stack_default_source gtk_status_stack_show_simple_message + + +/* Initialise la classe des barres de statut améliorées. */ +static void gtk_status_stack_class_init(GtkStatusStackClass *); + +/* Initialise une instance de barre de statut améliorée. */ +static void gtk_status_stack_init(GtkStatusStack *); + +/* Supprime toutes les références externes. */ +static void gtk_status_stack_dispose(GtkStatusStack *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_status_stack_finalize(GtkStatusStack *); + +/* Note le changement de verrouillage du stockage sécurisé. */ +static void gtk_status_stack_on_secret_storage_lock_update(GSecretStorage *, GtkStatusStack *); + +/* Met à jour dans la barre les débits réseau observés. */ +static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Met à jour une propriété d'instance GObject. */ +static void gtk_status_stack_set_property(GObject *, guint, const GValue *, GParamSpec *); + +/* Fournit la valeur d'une propriété d'instance GObject. */ +static void gtk_status_stack_get_property(GObject *, guint, GValue *, GParamSpec *); + + + +/* ----------------------- MISE EN AVANT DES MESSAGES SIMPLES ----------------------- */ + + +/* S'assure de l'affichage à jour de la partie "default". */ +static gboolean gtk_status_stack_show_simple_message(gpointer); + + + +/* -------------------- STATUT DES INFORMATIONS DE DESASSEMBLAGE -------------------- */ + + +/* Navigation au sein d'assemblage */ +struct _navigation_info_t +{ + char *segment; /* Segment d'appartenance */ + + mrange_t current; /* Emplacement correspondant */ + + VMPA_BUFFER(phys); /* Localisation physique */ + VMPA_BUFFER(virt); /* Localisation virtuelle */ + + char *symbol; /* Eventuel symbole concerné */ + + char *format; /* Architecture et format */ + char *details; /* Encodage de l'instruction */ + +}; + + +/* Met en place le suivi d'informations de navigation. */ +static void init_navigation_info(navigation_info_t *); + +/* Supprime l'empreinte mémoire d'informations de navigation. */ +static void fini_navigation_info(navigation_info_t *); + +/* S'assure de l'affichage à jour de la partie "navigation". */ +static gboolean gtk_status_stack_show_current_location(gpointer); + +/* Réagit à un clic sur l'icône de zoom. */ +static void gtk_status_stack_on_zoom_icon_press(GtkEntry *, GtkEntryIconPosition, GtkStatusStack *); + + + +/* -------------------------- STATUT DES SUIVIS D'ACTIVITE -------------------------- */ + + +/* Informations de progression */ +typedef struct _activity_status_t +{ + activity_id_t id; /* Identifiant unique */ + + char *message; /* Indication à faire valoir */ + + unsigned long current; /* Position courante */ + unsigned long max; /* Couverture à parcourir */ + + double last_updated; /* Dernière valeur poussée */ + +} activity_status_t; + + +/* Mémorisation des progressions au sein d'activités */ +struct _activity_info_t +{ + GMutex access; /* Accès à la pile */ + + activity_id_t generator; /* Générateur de séquence */ + + activity_status_t *statuses; /* Statuts de progression */ + size_t count; /* Nombre de ces statuts */ + + guint tag; /* Identifiant de mise à jour */ + +}; + + +/* Met en place le suivi d'informations d'activité. */ +static void init_activity_info(activity_info_t *); + +/* Supprime l'empreinte mémoire d'informations d'activité. */ +static void fini_activity_info(activity_info_t *); + +/* Recherche les indications de statut d'une activité donnée. */ +static activity_status_t *find_activity_status_by_id(activity_info_t *, activity_id_t); + +/* S'assure de l'affichage à jour de la partie "activité". */ +static gboolean gtk_status_stack_show_current_activity(gpointer); + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION GENERALE DES STATUTS */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des barres de statut améliorées. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_class_init(GtkStatusStackClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)gtk_status_stack_dispose; + object->finalize = (GObjectFinalizeFunc)gtk_status_stack_finalize; + object->set_property = gtk_status_stack_set_property; + object->get_property = gtk_status_stack_get_property; + + _status_stack_properties[PROP_SHOW_BOTTOM] = + g_param_spec_boolean("show-bottom", NULL, NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object, N_PROPERTIES, _status_stack_properties); + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/statusstack.ui"); + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_status_stack_on_zoom_icon_press)); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, main); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, def_label); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_segment); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_phys); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_virt); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_offset); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_format); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_details); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, zoom); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_message); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_progress); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, lock_update); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_recv_speed); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_send_speed); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, bottom_toggler); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = composant GTK à initialiser. * +* * +* Description : Initialise une instance de barre de statut améliorée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_init(GtkStatusStack *stack) +{ + gtk_widget_init_template(GTK_WIDGET(stack)); + + stack->def_source = gtk_status_stack_default_source; + + stack->msg = NULL; + + stack->nav_info = calloc(1, sizeof(navigation_info_t)); + init_navigation_info(stack->nav_info); + + stack->activity_info = calloc(1, sizeof(activity_info_t)); + init_activity_info(stack->activity_info); + + /* Suivi des évolutions relatives au stockage sécurisé */ + + stack->storage = get_secret_storage(); + + g_signal_connect(stack->storage, "lock-update", + G_CALLBACK(gtk_status_stack_on_secret_storage_lock_update), stack); + + gtk_status_stack_on_secret_storage_lock_update(stack->storage, stack); + + /* Suivi des débits de connexion */ + + stack->next_index = 0; + + stack->network_update_tag = g_timeout_add(NETWORK_UPDATE_INTERVAL, + G_SOURCE_FUNC(gtk_status_stack_update_network_stats), stack); + + /* Premier affichage... */ + + gtk_status_stack_reset_to_default(stack); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_dispose(GtkStatusStack *stack) +{ + if (stack->storage != NULL) + g_signal_handlers_disconnect_by_func(stack->storage, + gtk_status_stack_on_secret_storage_lock_update, stack); + + g_clear_object(&stack->storage); + + g_source_remove(stack->network_update_tag); + + gtk_widget_dispose_template(GTK_WIDGET(stack), GTK_TYPE_STATUS_STACK); + + G_OBJECT_CLASS(gtk_status_stack_parent_class)->dispose(G_OBJECT(stack)); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_finalize(GtkStatusStack *stack) +{ + if (stack->msg != NULL) + free(stack->msg); + + fini_navigation_info(stack->nav_info); + free(stack->nav_info); + + fini_activity_info(stack->activity_info); + free(stack->activity_info); + + G_OBJECT_CLASS(gtk_status_stack_parent_class)->finalize(G_OBJECT(stack)); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée une nouvelle instance de barre de statut. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkStatusStack *gtk_status_stack_new(void) +{ + GtkStatusStack *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_STATUS_STACK, NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* * +* Description : Réinitialise la barre de statut à son stade par défaut. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_reset_to_default(GtkStatusStack *stack) +{ + /** + * Une amélioration possible serait de passer à g_idle_add_once(), + * et de s'affranchir des retours G_SOURCE_REMOVE systématiques, + * mais la fonction n'est disponible qu'à partir de la GLib 2.74. + */ + + g_idle_add(stack->def_source, stack); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gardien des secrets impliqué. * +* stack = barre de statut à actualiser. * +* * +* Description : Note le changement de verrouillage du stockage sécurisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_on_secret_storage_lock_update(GSecretStorage *storage, GtkStatusStack *stack) +{ + if (!g_secret_storage_has_key(stack->storage)) + { + gtk_widget_set_sensitive(GTK_WIDGET(stack->lock_update), false); + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "nolock-symbolic"); + } + else + { + gtk_widget_set_sensitive(GTK_WIDGET(stack->lock_update), true); + + if (g_secret_storage_is_locked(stack->storage)) + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "locked-symbolic"); + else + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "unlocked-symbolic"); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* * +* Description : Met à jour dans la barre les débits réseau observés. * +* * +* Retour : G_SOURCE_CONTINUE pour poursuivre les mises à jour. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *stack) +{ + gboolean result; /* Indication à retourner */ + gint64 timestamp; /* Position temporelle */ + size_t received; /* Quantité d'octets reçus */ + size_t sent; /* Quantité d'octets émis */ + gint64 diff_time; /* Différentiel de temps */ + size_t diff_bytes; /* Différentiel de volume */ + double speed; /* Débit de transfert constaté */ + size_t i; /* Boucle de parcours */ + char *value; /* Valeur à afficher */ + + const char *units[] = { _("b/s"), _("kb/s"), _("Mb/s"), _("Gb/s"), _("Tb/s") }; + + result = G_SOURCE_CONTINUE; + + /* Mémorisation des données */ + + timestamp = g_get_monotonic_time(); + + get_network_stats(&received, &sent); + + if (stack->next_index < NETWORK_UPDATE_COUNT) + { + stack->last_bytes_received[stack->next_index] = received; + stack->last_bytes_sent[stack->next_index] = sent; + stack->last_timestamps[stack->next_index] = timestamp; + + stack->next_index++; + + } + else + { + memcpy(stack->last_bytes_received, stack->last_bytes_received + 1, + (NETWORK_UPDATE_COUNT - 1) * sizeof(size_t)); + + memcpy(stack->last_bytes_sent, stack->last_bytes_sent + 1, + (NETWORK_UPDATE_COUNT - 1) * sizeof(size_t)); + + memcpy(stack->last_timestamps, stack->last_timestamps + 1, + (NETWORK_UPDATE_COUNT - 1) * sizeof(gint64)); + + stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] = received; + stack->last_bytes_sent[NETWORK_UPDATE_COUNT - 1] = sent; + stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] = timestamp; + + } + + if (stack->next_index < NETWORK_UPDATE_COUNT) + goto done; + + diff_time = stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] - stack->last_timestamps[0]; + + /* Débit de réception */ + + diff_bytes = stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_received[0]; + + speed = (diff_bytes * 1000000) / diff_time; + + for (i = 0; i < G_N_ELEMENTS(units); i++) + { + if (speed < 1024) + break; + + speed /= 1024; + + } + + if (i == 0) + asprintf(&value, "%d %s", (int)speed, units[i]); + else + asprintf(&value, "%.1f %s", speed, units[i]); + + gtk_label_set_label(stack->net_recv_speed, value); + + free(value); + + /* Débit de émission */ + + diff_bytes = stack->last_bytes_sent[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_sent[0]; + + speed = (diff_bytes * 1000000) / diff_time; + + for (i = 0; i < G_N_ELEMENTS(units); i++) + { + if (speed < 1024) + break; + + speed /= 1024; + + } + + if (i == 0) + asprintf(&value, "%d %s", (int)speed, units[i]); + else + asprintf(&value, "%.1f %s", speed, units[i]); + + gtk_label_set_label(stack->net_send_speed, value); + + free(value); + + done: + + return result; + +} + + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à prendre en compte. * +* pspec = définition de la propriété. * +* * +* Description : Met à jour une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GtkStatusStack *stack; /* Version spécialisée */ + + stack = GTK_STATUS_STACK(object); + + switch (prop_id) + { + case PROP_SHOW_BOTTOM: + gtk_toggle_button_set_active(stack->bottom_toggler, g_value_get_boolean(value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + + } + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à transmettre. [OUT] * +* pspec = définition de la propriété. * +* * +* Description : Fournit la valeur d'une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GtkStatusStack *stack; /* Version spécialisée */ + + stack = GTK_STATUS_STACK(object); + + switch (prop_id) + { + case PROP_SHOW_BOTTOM: + g_value_set_boolean(value, gtk_toggle_button_get_active(stack->bottom_toggler)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + + } + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MISE EN AVANT DES MESSAGES SIMPLES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* msg = simple message à faire paraître. * +* * +* Description : Inscrit un message simple dans la barre de statut. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_display_message(GtkStatusStack *stack, const char *msg) +{ + if (stack->msg != NULL) + free(stack->msg); + + if (msg != NULL) + stack->msg = strdup(msg); + else + stack->msg = NULL; + + gtk_status_stack_show_simple_message(stack); + +} + + +/****************************************************************************** +* * +* Paramètres : data = pile de statuts à manipuler. * +* * +* Description : S'assure de l'affichage à jour de la partie "default". * +* * +* Retour : G_SOURCE_REMOVE pour une exécution unique. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_status_stack_show_simple_message(gpointer data) +{ + GtkStatusStack *stack; /* Version spécialisée */ + + stack = GTK_STATUS_STACK(data); + + stack->def_source = gtk_status_stack_show_simple_message; + + gtk_label_set_text(stack->def_label, stack->msg != NULL ? stack->msg : ""); + + gtk_stack_set_visible_child_name(stack->main, "default"); + + return G_SOURCE_REMOVE; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* STATUT DES INFORMATIONS DE DESASSEMBLAGE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : info = informations à initialiser. * +* * +* Description : Met en place le suivi d'informations de navigation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void init_navigation_info(navigation_info_t *info) +{ + info->segment = NULL; + + info->symbol = NULL; + + info->format = NULL; + info->details = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations à libérer de la mémoire. * +* * +* Description : Supprime l'empreinte mémoire d'informations de navigation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void fini_navigation_info(navigation_info_t *info) +{ + if (info->segment != NULL) + free(info->segment); + + if (info->symbol != NULL) + free(info->symbol); + + if (info->format != NULL) + free(info->format); + + if (info->details != NULL) + free(info->details); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* range = emplacement à mettre en valeur. * +* segment = zone de binaire d'appartenance. * +* symbol = éventuelle position par rapport à un symbole. * +* format = format du binaire manipulé * +* details = détails supplémentaires (liés à l'encodage ?) * +* * +* Description : Actualise les informations liées une position d'assemblage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_update_current_location(GtkStatusStack *stack, const mrange_t *range, const char *segment, const char *symbol, const char *format, const char *details) +{ + navigation_info_t *info; /* Informations à constituer */ + const vmpa2t *addr; /* Localisation de départ */ + + info = stack->nav_info; + + if (cmp_mrange(&info->current, range) == 0) + goto useless; + + /* Zone d'appartenance */ + + if (segment != NULL) + info->segment = strdup(segment); + else + info->segment = NULL; + + /* Adresses de base */ + + copy_mrange(&info->current, range); + + addr = get_mrange_addr(range); + + vmpa2_phys_to_string(addr, MDS_UNDEFINED, info->phys, NULL); + + vmpa2_virt_to_string(addr, MDS_UNDEFINED, info->virt, NULL); + + /* Symbole concerné */ + + if (symbol != NULL) + info->symbol = strdup(symbol); + else + info->symbol = NULL; + + /* Architecture & format */ + + info->format = strdup(format); + + if (details != NULL) + info->details = strdup(details); + else + info->details = NULL; + + /* Nettoyage et conclusion */ + + gtk_status_stack_show_current_location(stack); + + useless: + + ; + +} + + +/****************************************************************************** +* * +* Paramètres : data = pile de statuts à manipuler. * +* * +* Description : S'assure de l'affichage à jour de la partie "navigation". * +* * +* Retour : G_SOURCE_REMOVE pour une exécution unique. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_status_stack_show_current_location(gpointer data) +{ + GtkStatusStack *stack; /* Version spécialisée */ + navigation_info_t *info; /* Informations à constituer */ + char raw_pos[6 + VMPA_MAX_LEN + 1]; /* Formatage final en direct */ + + stack = GTK_STATUS_STACK(data); + + stack->def_source = gtk_status_stack_show_current_location; + + info = stack->nav_info; + + /* Première partie : navigation */ + + gtk_label_set_text(stack->nav_segment, info->segment != NULL ? info->segment : ""); + + snprintf(raw_pos, sizeof(raw_pos), "phys: %s", info->phys); + + gtk_label_set_text(stack->nav_phys, raw_pos); + + snprintf(raw_pos, sizeof(raw_pos), "virt: %s", info->virt); + + gtk_label_set_text(stack->nav_virt, raw_pos); + + gtk_label_set_text(stack->nav_offset, info->symbol != NULL ? info->symbol : ""); + + /* Seconde partie : format & architecture */ + + gtk_label_set_text(stack->nav_format, info->format); + + gtk_label_set_text(stack->nav_details, info->details != NULL ? info->details : ""); + + /* Conclusion */ + + gtk_stack_set_visible_child_name(stack->main, "navigation"); + + return G_SOURCE_REMOVE; + +} + + +/****************************************************************************** +* * +* Paramètres : entry = zone de texte visée par la procédure. * +* icon_pos = position de l'image associée à l'entrée. * +* stack = composant graphique de gestion des statuts. * +* * +* Description : Réagit à un clic sur l'icône de zoom. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_on_zoom_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GtkStatusStack *stack) +{ +#if 0 + GtkWidget *popup; /* Popup à faire surgir */ + GdkRectangle rect; /* Zone précise à cibler */ + + popup = gtk_popover_new(); + + gtk_entry_get_icon_area(entry, GTK_ENTRY_ICON_SECONDARY, &rect); + gtk_popover_set_pointing_to(GTK_POPOVER(popup), &rect); + + gtk_widget_show(popup); +#endif +} + + +/* ---------------------------------------------------------------------------------- */ +/* STATUT DES SUIVIS D'ACTIVITE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : info = informations à initialiser. * +* * +* Description : Met en place le suivi d'informations d'activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void init_activity_info(activity_info_t *info) +{ + g_mutex_init(&info->access); + + info->generator = NO_ACTIVITY_ID; + + info->statuses = NULL; + info->count = 0; + + info->tag = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations à libérer de la mémoire. * +* * +* Description : Supprime l'empreinte mémoire d'informations d'activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void fini_activity_info(activity_info_t *info) +{ + size_t i; /* Boucle de parcours */ + + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = 0; + + for (i = 0; i < info->count; i++) + { + if (info->statuses[i].message != NULL) + free(info->statuses[i].message); + } + + if (info->statuses != NULL) + { + free(info->statuses); + info->statuses = NULL; + } + + info->count = 0; + + g_mutex_clear(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations relatives aux activités à consulter. * +* id = identifiant de l'activité à cibler. * +* * +* Description : Recherche les indications de statut d'une activité donnée. * +* * +* Retour : Structure d'encadrement trouvée ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static activity_status_t *find_activity_status_by_id(activity_info_t *info, activity_id_t id) +{ + activity_status_t *result; /* Statut trouvé à renvoyer */ + size_t i; /* Boucle de parcours */ + + result = NULL; + + assert(!g_mutex_trylock(&info->access)); + + for (i = 0; i < info->count; i++) + if (info->statuses[i].id == id) + { + result = info->statuses + i; + break; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* msg = nouveau message de statut à copier. * +* max = taille de la plage à parcourir. * +* * +* Description : Démarre le suivi d'une nouvelle activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *msg, unsigned long max) +{ + activity_id_t result; /* Numéro unique à renvoyer */ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *new; /* Nouveau suivi d'activité */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + while (1) + { + result = ++info->generator; + + if (find_activity_status_by_id(info, result) == NULL) + break; + + } + while (0); + + info->statuses = realloc(info->statuses, ++info->count * sizeof(activity_status_t)); + + new = info->statuses + info->count - 1; + + /* Identifiant */ + + new->id = result; + + /* Intitulé */ + + if (msg == NULL) + new->message = NULL; + else + new->message = strdup(msg); + + /* Valeur */ + + new->current = 0; + new->max = max; + new->last_updated = 0; + + /* Actualisation */ + + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + g_mutex_unlock(&info->access); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* id = identifiant de l'activité à cibler. * +* msg = nouveau message de statut à copier. * +* * +* Description : Actualise les informations concernant une activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_update_activity_message(GtkStatusStack *stack, activity_id_t id, const char *msg) +{ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *status; /* Suivi d'activité à traiter */ + bool msg_changed; /* Changement d'intitulé */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + status = find_activity_status_by_id(info, id); + + assert(status != NULL); + + if (status == NULL) + goto exit; + + /* Intitulé */ + + if (status->message != NULL) + { + if (msg == NULL) + msg_changed = true; + else + msg_changed = (strcmp(status->message, msg) != 0); + + free(status->message); + + } + else + msg_changed = (msg != NULL); + + if (msg == NULL) + status->message = NULL; + else + status->message = strdup(msg); + + /* On n'actualise que le sommet de la pile */ + + if ((status - info->statuses + 1) == info->count && msg_changed) + { + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + } + + exit: + + g_mutex_unlock(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* id = identifiant de l'activité à cibler. * +* inc = nouvelle valeur pour une progression donnée. * +* * +* Description : Actualise la progression d'une activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t id, unsigned long inc) +{ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *status; /* Suivi d'activité à traiter */ + double new; /* Nouvelle progression */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + status = find_activity_status_by_id(info, id); + + assert(status != NULL); + + if (status == NULL) + goto exit; + + /* Valeur */ + + status->current += inc; + + new = (status->current * 1.0) / status->max; + + /* On n'actualise que le sommet de la pile */ + + if ((status - info->statuses + 1) == info->count && (new - status->last_updated) > 0.1) + { + status->last_updated = new; + + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + } + + exit: + + g_mutex_unlock(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* id = identifiant de l'activité à cibler. * +* extra = nouvelle échéance supplémentaire des traitements. * +* * +* Description : Etend la portée des travaux d'une nouvelle activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_extend_activity_max(GtkStatusStack *stack, activity_id_t id, unsigned long extra) +{ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *status; /* Suivi d'activité à traiter */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + status = find_activity_status_by_id(info, id); + + assert(status != NULL); + + if (status == NULL) + goto exit; + + /* Valeur */ + + status->max += extra; + + /* On n'actualise que le sommet de la pile */ + + if ((status - info->statuses + 1) == info->count) + { + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + } + + exit: + + g_mutex_unlock(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* * +* Description : Met fin au suivi d'une activité donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_remove_activity(GtkStatusStack *stack, activity_id_t id) +{ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *status; /* Suivi d'activité à traiter */ + bool is_last; /* Dernière position ? */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + status = find_activity_status_by_id(info, id); + + assert(status != NULL); + + if (status == NULL) + goto exit; + + is_last = ((status - info->statuses + 1) == info->count); + + /* Suppression des données */ + + if (is_last) + { + if (info->tag != 0) + g_source_remove(info->tag); + } + + if (status->message != NULL) + free(status->message); + + /* Réajustement des enregistrements */ + + if (!is_last) + memmove(status, status + 1, + (((info->statuses + info->count) - status) - 1) * sizeof(activity_status_t)); + + info->statuses = realloc(info->statuses, --info->count * sizeof(activity_status_t)); + + /* Bascule vers un autre affichage ou actualisation ? */ + + if (info->count == 0) + { + info->tag = 0; + gtk_status_stack_reset_to_default(stack); + } + else if (is_last) + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + exit: + + g_mutex_unlock(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : data = pile de statuts à manipuler. * +* * +* Description : S'assure de l'affichage à jour de la partie "activité". * +* * +* Retour : G_SOURCE_REMOVE pour une exécution unique. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_status_stack_show_current_activity(gpointer data) +{ + GtkStatusStack *stack; /* Version spécialisée */ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *last; /* Dernier statut à traiter */ + + stack = GTK_STATUS_STACK(data); + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + if (!g_source_is_destroyed(g_main_current_source())) + { + if (info->count > 0) + { + last = &info->statuses[info->count - 1]; + + gtk_label_set_text(stack->activity_message, last->message); + + gtk_progress_bar_set_fraction(stack->activity_progress, (last->current * 1.0) / last->max); + + gtk_stack_set_visible_child_name(stack->main, "activity"); + + } + + info->tag = 0; + + } + + g_mutex_unlock(&info->access); + + return G_SOURCE_REMOVE; + +} diff --git a/src/gtkext/gtkstatusstack.h b/src/gtkext/statusstack.h index f419014..96d008c 100644 --- a/src/gtkext/gtkstatusstack.h +++ b/src/gtkext/statusstack.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * gtkstatusstack.h - prototypes pour un empilement d'informations de statut + * statusstack.h - prototypes pour l'empilement d'informations de statut * - * Copyright (C) 2015-2019 Cyrille Bagard + * Copyright (C) 2015-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,40 +21,39 @@ */ -#ifndef _GTKEXT_GTKSTATUSSTACK_H -#define _GTKEXT_GTKSTATUSSTACK_H +#ifndef _GTKEXT_STATUSSTACK_H +#define _GTKEXT_STATUSSTACK_H #include <gtk/gtk.h> #include "../arch/vmpa.h" +#include "../glibext/helpers.h" -/* ------------------------- GESTION EXTERIEURE DE LA BARRE ------------------------- */ +/* -------------------------- GESTION GENERALE DES STATUTS -------------------------- */ -#define GTK_TYPE_STATUS_STACK (gtk_status_stack_get_type()) -#define GTK_STATUS_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_STATUS_STACK, GtkStatusStack)) -#define GTK_STATUS_STACK_CLASS(klass) (G_LOADED_BINARY_GET_CLASS(klass, GTK_TYPE_STATUS_STACK, GtkStatusStackClass)) -#define GTK_IS_STATUS_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_STATUS_STACK)) -#define GTK_IS_STATUS_STACK_CLASS(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_STATUS_STACK)) -#define GTK_STATUS_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_STATUS_STACK, GtkStatusStackClass)) +#define GTK_TYPE_STATUS_STACK (gtk_status_stack_get_type()) +DECLARE_GTYPE(GtkStatusStack, gtk_status_stack, GTK, STATUS_STACK); -/* Abstration d'une gestion de barre de statut (instance) */ -typedef struct _GtkStatusStack GtkStatusStack; -/* Abstration d'une gestion de barre de statut (classe) */ -typedef struct _GtkStatusStackClass GtkStatusStackClass; +/* Crée une nouvelle instance de barre de statut. */ +GtkStatusStack *gtk_status_stack_new(void); +/* Réinitialise la barre de statut à son stade par défaut. */ +void gtk_status_stack_reset_to_default(GtkStatusStack *); -/* Détermine le type de la barre de statut améliorée. */ -GType gtk_status_stack_get_type(void); -/* Crée une nouvelle instance de barre de statut. */ -GtkStatusStack *gtk_status_stack_new(void); + +/* ----------------------- MISE EN AVANT DES MESSAGES SIMPLES ----------------------- */ + + +/* Inscrit un message simple dans la barre de statut. */ +void gtk_status_stack_display_message(GtkStatusStack *, const char *); @@ -62,10 +61,7 @@ GtkStatusStack *gtk_status_stack_new(void); /* Actualise les informations liées une position d'assemblage. */ -void gtk_status_stack_update_current_location(GtkStatusStack *, const mrange_t *, const char *, const char *, const char *); - -/* Réinitialise les informations associées une position. */ -void gtk_status_stack_reset_current_location(GtkStatusStack *); +void gtk_status_stack_update_current_location(GtkStatusStack *, const mrange_t *, const char *, const char *, const char *, const char *); @@ -82,18 +78,18 @@ typedef unsigned long activity_id_t; /* Démarre le suivi d'une nouvelle activité. */ activity_id_t gtk_status_stack_add_activity(GtkStatusStack *, const char *, unsigned long); -/* Etend la portée des travaux d'une nouvelle activité. */ -void gtk_status_stack_extend_activity(GtkStatusStack *, activity_id_t, unsigned long); - /* Actualise les informations concernant une activité. */ -void gtk_status_stack_update_activity(GtkStatusStack *, activity_id_t, const char *); +void gtk_status_stack_update_activity_message(GtkStatusStack *, activity_id_t, const char *); /* Actualise la progression d'une activité. */ void gtk_status_stack_update_activity_value(GtkStatusStack *, activity_id_t, unsigned long); +/* Etend la portée des travaux d'une nouvelle activité. */ +void gtk_status_stack_extend_activity_max(GtkStatusStack *, activity_id_t, unsigned long); + /* Met fin au suivi d'une activité donnée. */ void gtk_status_stack_remove_activity(GtkStatusStack *, activity_id_t); -#endif /* _GTKEXT_GTKSTATUSSTACK_H */ +#endif /* _GTKEXT_STATUSSTACK_H */ diff --git a/src/gtkext/statusstack.ui b/src/gtkext/statusstack.ui new file mode 100644 index 0000000..0b7cd6d --- /dev/null +++ b/src/gtkext/statusstack.ui @@ -0,0 +1,214 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="GtkStatusStack" parent="GtkBox"> + + <!--property name="show-bottom" bind-source="bottom_toggler" bind-property="active" bind-flags="bidirectional|sync-create"/--> + + <child> + <object class="GtkStack" id="main"> + <property name="margin-start">8</property> + <property name="margin-end">8</property> + <property name="hexpand">true</property> + + <!-- Vide par défaut --> + <child> + <object class="GtkStackPage"> + <property name="name">default</property> + <property name="child"> + + <object class="GtkLabel" id="def_label"> + <property name="hexpand">true</property> + <property name="halign">fill</property> + <property name="valign">baseline</property> + <property name="xalign">0</property> + <property name="label"></property> + <style> + <class name="dim-label"/> + </style> + </object> + + </property> + </object> + </child> + + <!-- Informations pendant une navigation dans du contenu --> + <child> + <object class="GtkStackPage"> + <property name="name">navigation</property> + <property name="child"> + + <object class="GtkBox"> + <property name="orientation">horizontal</property> + <property name="hexpand">true</property> + <property name="halign">fill</property> + <property name="valign">center</property> + + <!-- Première partie : navigation --> + + <child> + <object class="GtkLabel" id="nav_segment"> + <property name="xalign">0</property> + <property name="label">-</property> + </object> + </child> + + <child> + <object class="GtkLabel" id="nav_phys"> + <property name="xalign">0</property> + <property name="label">-</property> + </object> + </child> + + <child> + <object class="GtkLabel" id="nav_virt"> + <property name="xalign">0</property> + <property name="label">-</property> + </object> + </child> + + <child> + <object class="GtkLabel" id="nav_offset"> + <property name="xalign">0</property> + <property name="hexpand">true</property> + <property name="halign">fill</property> + <property name="label">-</property> + </object> + </child> + + <!-- Seconde partie : architecture --> + + <child> + <object class="GtkLabel" id="nav_format"> + <property name="xalign">0</property> + <property name="label">-</property> + </object> + </child> + + <child> + <object class="GtkLabel" id="nav_details"> + <property name="xalign">0</property> + <property name="label">-</property> + </object> + </child> + + <!-- Troisième partie : affichage --> + + <child> + <object class="GtkEntry" id="zoom"> + <property name="secondary-icon-name">go-up-symbolic</property> + <signal name="icon-press" handler="gtk_status_stack_on_zoom_icon_press"/> + </object> + </child> + + </object> + + </property> + </object> + </child> + + <!-- Informations liées à une activité en cours --> + <child> + <object class="GtkStackPage"> + <property name="name">activity</property> + <property name="child"> + + <object class="GtkBox"> + <property name="orientation">horizontal</property> + <property name="hexpand">true</property> + <property name="halign">fill</property> + <property name="valign">center</property> + + <child> + <object class="GtkLabel" id="activity_message"> + <property name="xalign">0</property> + <property name="margin-end">8</property> + <property name="label">-</property> + </object> + </child> + + <child> + <object class="GtkProgressBar" id="activity_progress"> + <property name="hexpand">true</property> + <property name="halign">fill</property> + <property name="valign">center</property> + <property name="fraction">0</property> + </object> + </child> + + </object> + + </property> + </object> + </child> + + <property name="visible-child-name">default</property> + </object> + </child> + + <!-- Tronc commun --> + + <child> + <object class="GtkSeparator"> + <property name="orientation">vertical</property> + </object> + </child> + + <child> + <object class="GtkToggleButton" id="lock_update"> + <property name="sensitive">false</property> + <property name="has-frame">false</property> + <property name="icon-name">nolock-symbolic</property> + </object> + </child> + + <child> + <object class="GtkSeparator"> + <property name="orientation">vertical</property> + </object> + </child> + + <child> + <object class="GtkImage"> + <property name="margin-start">8</property> + <property name="icon-name">pan-down-symbolic</property> + </object> + </child> + <child> + <object class="GtkLabel" id="net_recv_speed"> + <property name="margin-start">8</property> + <property name="margin-end">8</property> + <property name="xalign">0</property> + <property name="label">0 b/s</property> + </object> + </child> + + <child> + <object class="GtkImage"> + <property name="icon-name">pan-up-symbolic</property> + </object> + </child> + <child> + <object class="GtkLabel" id="net_send_speed"> + <property name="margin-start">8</property> + <property name="margin-end">8</property> + <property name="xalign">0</property> + <property name="label">0 b/s</property> + </object> + </child> + + <child> + <object class="GtkSeparator"> + <property name="orientation">vertical</property> + </object> + </child> + + <child> + <object class="GtkToggleButton" id="bottom_toggler"> + <property name="has-frame">false</property> + <property name="icon-name">dock-station-bottom-symbolic</property> + <property name="action-name">win.toggle-bottom</property> + </object> + </child> + + </template> +</interface> diff --git a/src/gtkext/tiledgrid.c b/src/gtkext/tiledgrid.c deleted file mode 100644 index 22b2680..0000000 --- a/src/gtkext/tiledgrid.c +++ /dev/null @@ -1,1143 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * tiledgrid.c - composant d'affichage avec des chemins vers les composants contenus - * - * Copyright (C) 2018-2019 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "tiledgrid.h" - - -#include <assert.h> -#include <ctype.h> -#include <malloc.h> -#include <string.h> - - -#include "../core/logs.h" - - - -/* -------------------------- GESTION DES TUILES AFFICHEES -------------------------- */ - - -/* Informations concernant une tuile */ -typedef struct _grid_tile_t -{ - struct _grid_tile_t *parent; /* Tuile parente */ - - GtkWidget *widget; /* Support d'affichage */ - - char *path; /* Chemin d'accès */ - - struct _grid_tile_t *children[2]; /* Tuiles encastrées ou 2xNULL */ - -} grid_tile_t; - - -#define IS_LEAF_TILE(t) \ - ({ \ - bool __result; \ - __result = GTK_IS_DOCK_STATION((t)->widget); \ - assert(__result || GTK_IS_PANED((t)->widget)); \ - __result; \ - }) - - -/* Valide un chemin d'accès à une tuile. */ -static bool is_valid_tile_path(const char *); - -/* Crée une tuile finale d'affichage de panneaux. */ -static grid_tile_t *create_leaf_tile(const char *, GtkTiledGrid *); - -/* Crée une tuile intermédiaire d'affichage de panneaux. */ -static grid_tile_t *create_inter_tile(grid_tile_t *, bool, grid_tile_t *, grid_tile_t *); - -/* Supprime une tuile de la mémoire. */ -static void delete_tile(grid_tile_t *); - -/* Calcule la taille comme entre un chemin et celui d'une tuile. */ -static size_t compute_tile_score(const grid_tile_t *, const char *); - -/* Indique la tuile adaptée pour un chemin donné. */ -static grid_tile_t *find_suitable_tile(grid_tile_t **, const char *, GtkTiledGrid *); - -/* Découpe une tuile pour y insérer une zone. */ -static grid_tile_t *split_tile(grid_tile_t **, const char *, char, GtkTiledGrid *); - -/* Tente de mettre la main sur une station d'accueil. */ -static grid_tile_t *find_tile_for_widget(grid_tile_t *, GtkWidget *); - -/* Retire une moitié de tuile vide au plein profit de l'autre. */ -static void collapse_tile(grid_tile_t *, grid_tile_t *); - - - -/* --------------------------- INTERFACE DU COMPOSANT GTK --------------------------- */ - - -/* Conteneur pour un affichage en tuiles nommées (instance) */ -struct _GtkTiledGrid -{ - GtkBin parent; /* A laisser en premier */ - - grid_tile_t *tiles; /* Tuiles représentées */ - - GPanelItem *def_panel; /* Panneau principal par défaut*/ - -}; - -/* Conteneur pour un affichage en tuiles nommées (classe) */ -struct _GtkTiledGridClass -{ - GtkBinClass parent; /* A laisser en premier */ - - /* Signaux */ - - void (* station_created) (GtkTiledGrid *, GtkDockStation *, gpointer); - -}; - - -/* Initialise la classe des conteneurs d'affichage en tuiles. */ -static void gtk_tiled_grid_class_init(GtkTiledGridClass *); - -/* Initialise une instance de conteneur d'affichage en tuiles. */ -static void gtk_tiled_grid_init(GtkTiledGrid *); - -/* Supprime toutes les références externes. */ -static void gtk_tiled_grid_dispose(GtkTiledGrid *); - -/* Procède à la libération totale de la mémoire. */ -static void gtk_tiled_grid_finalize(GtkTiledGrid *); - - - -/* ---------------------------------------------------------------------------------- */ -/* GESTION DES TUILES AFFICHEES */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : path = chemin destiné à sélectionner une tuile. * -* * -* Description : Valide un chemin d'accès à une tuile. * -* * -* Retour : true si le chemin est utilisable, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool is_valid_tile_path(const char *path) -{ - bool result; /* Bilan à retourner */ - size_t len; /* Taille du chemin */ - size_t i; /* Boucle de parcours */ - char c; /* Caractère de chemin analysé */ - - /** - * M[NESWnesw]* - */ - - len = strlen(path); - - result = (len >= 1); - - if (result) - result = (path[0] == 'M'); - - for (i = 1; i < len && result; i++) - { - c = path[i]; - - if (c == '\0') - break; - - result = (c == 'N' || c == 'n' - || c == 'E' || c == 'e' - || c == 'S' || c == 's' - || c == 'W' || c == 'w'); - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : path = chemin d'accès à la future tuile. * -* Paramètres : tgrid = conteneur d'affichage en tuiles à manipuler. * -* * -* Description : Crée une tuile finale d'affichage de panneaux. * -* * -* Retour : Structure mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static grid_tile_t *create_leaf_tile(const char *path, GtkTiledGrid *tgrid) -{ - grid_tile_t *result; /* Structure à retourner */ - - result = (grid_tile_t *)malloc(sizeof(grid_tile_t)); - - result->parent = NULL; - - result->widget = gtk_dock_station_new(); - gtk_widget_show(result->widget); - - result->path = strdup(path); - - result->children[0] = NULL; - result->children[1] = NULL; - - g_signal_emit_by_name(tgrid, "station-created", result->widget); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : parent = tuile parente ou NULL si aucune. * -* horiz = indique le type d'orientation désiré. * -* first = première tuile à intégrer. * -* second = seconde tuile à intégrer. * -* * -* Description : Crée une tuile intermédiaire d'affichage de panneaux. * -* * -* Retour : Structure mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static grid_tile_t *create_inter_tile(grid_tile_t *parent, bool horiz, grid_tile_t *first, grid_tile_t *second) -{ - grid_tile_t *result; /* Structure à retourner */ - GtkWidget *container; /* Conteneur à vider */ - - result = (grid_tile_t *)malloc(sizeof(grid_tile_t)); - - result->parent = parent; - - if (horiz) - result->widget = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); - else - result->widget = gtk_paned_new(GTK_ORIENTATION_VERTICAL); - - gtk_widget_show(result->widget); - - result->path = NULL; - - result->children[0] = first; - result->children[1] = second; - - /* Changement de propriétaire */ - - container = gtk_widget_get_parent(first->widget); - - if (container != NULL) - gtk_container_remove(GTK_CONTAINER(container), first->widget); - - g_object_ref(G_OBJECT(first->widget)); - gtk_paned_pack1(GTK_PANED(result->widget), first->widget, TRUE, FALSE); - - container = gtk_widget_get_parent(second->widget); - - if (container != NULL) - gtk_container_remove(GTK_CONTAINER(container), second->widget); - - g_object_ref(G_OBJECT(second->widget)); - gtk_paned_pack2(GTK_PANED(result->widget), second->widget, TRUE, FALSE); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : tile = tuile à supprimer. * -* * -* Description : Supprime une tuile de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void delete_tile(grid_tile_t *tile) -{ - if (!IS_LEAF_TILE(tile)) - { - delete_tile(tile->children[0]); - delete_tile(tile->children[1]); - } - - else - free(tile->path); - - g_object_unref(G_OBJECT(tile->widget)); - - free(tile); - -} - - -/****************************************************************************** -* * -* Paramètres : tile = tuile à analyser. * -* path = chemin final complet recherché. * -* * -* Description : Calcule la taille comme entre un chemin et celui d'une tuile.* -* * -* Retour : Quantité de caractères communs. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static size_t compute_tile_score(const grid_tile_t *tile, const char *path) -{ - size_t result; /* Nombre de points à renvoyer */ - size_t max; /* Taille du chemin de la tuile*/ - size_t i; /* Boucle de parcours */ - size_t score_0; /* Score du sous-élément #1 */ - size_t score_1; /* Score du sous-élément #2 */ - - if (IS_LEAF_TILE(tile)) - { - max = strlen(tile->path); - - if (strlen(path) < max) - result = 0; - - else - { - result = 0; - - for (i = 0; i < max; i++) - { - if (tolower((unsigned char)tile->path[i]) == tolower((unsigned char)path[i])) - result++; - else - break; - } - - } - - } - else - { - score_0 = compute_tile_score(tile->children[0], path); - score_1 = compute_tile_score(tile->children[1], path); - - result = score_0 > score_1 ? score_0 : score_1; - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : tile = tuile ou NULL si aucune. [OUT] * -* path = chemin d'accès à la tuile visée. * -* tgrid = conteneur d'affichage en tuiles à manipuler. * -* * -* Description : Indique la tuile adaptée pour un chemin donné. * -* * -* Retour : Structure d'acceuil à disposition. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static grid_tile_t *find_suitable_tile(grid_tile_t **tile, const char *path, GtkTiledGrid *tgrid) -{ - grid_tile_t *result; /* Structure à renvoyer */ - size_t best_len; /* Taille du chemin associé */ - size_t score_0; /* Score du sous-élément #1 */ - size_t score_1; /* Score du sous-élément #2 */ - char *sub_path; /* Nouvelle tentative d'accès */ - grid_tile_t **best; /* Direction à prendre */ - unsigned char next; /* Prochaine étape */ - - /* Cas d'école : appel initial */ - if (*tile == NULL) - { - assert(path[0] == 'M' && path[1] == '\0'); - - result = create_leaf_tile("M", tgrid); - *tile = result; - - } - - else - { - if (IS_LEAF_TILE(*tile)) - { - best_len = compute_tile_score(*tile, path); - - assert(best_len > 0); - - if (path[best_len] == '\0') - result = *tile; - - else - result = split_tile(tile, path, path[best_len], tgrid); - - } - - else - { - score_0 = compute_tile_score((*tile)->children[0], path); - score_1 = compute_tile_score((*tile)->children[1], path); - - assert(score_0 > 0 || score_0 > 0); - - if (score_0 == score_1) - { - sub_path = strndup(path, score_0); - - score_0 = compute_tile_score((*tile)->children[0], sub_path); - score_1 = compute_tile_score((*tile)->children[1], sub_path); - - free(sub_path); - - } - - if (score_0 == score_1) - result = split_tile(tile, path, path[score_0], tgrid); - - else - { - if (score_0 > score_1) - { - best = &(*tile)->children[0]; - best_len = score_0; - } - else - { - best = &(*tile)->children[1]; - best_len = score_1; - } - - /** - * Si on vient de tomber une feuille, trois cas de figure : - * - soit c'est elle qui est visée. - * - soit on veut la diviser. - * - soit on veut la diviser en englobant ses voisines. - */ - - if (IS_LEAF_TILE(*best)) - { - assert(best_len <= strlen(path)); - - next = path[best_len]; - - /* Premier cas */ - if (next == '\0') - result = *best; - - else - { - /* Second cas */ - if (islower(next)) - result = find_suitable_tile(best, path, tgrid); - - /* Troisième cas */ - else - result = split_tile(tile, path, next, tgrid); - - } - - } - - else - result = find_suitable_tile(best, path, tgrid); - - } - - } - - } - - assert(IS_LEAF_TILE(result)); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : tile = tuile à découper en deux. [OUT] * -* path = chemin d'accès à la future tuile. * -* endpoint = désignation de la zone représentée. * -* tgrid = conteneur d'affichage en tuiles à manipuler. * -* * -* Description : Découpe une tuile pour y insérer une zone. * -* * -* Retour : Structure fille mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static grid_tile_t *split_tile(grid_tile_t **tile, const char *path, char endpoint, GtkTiledGrid *tgrid) -{ - grid_tile_t *result; /* Création à retourner */ - GtkWidget *container; /* Conteneur à vider */ - grid_tile_t *new; /* Nouvelle tuile intermédiaire*/ - - container = gtk_widget_get_parent((*tile)->widget); - - /* Création */ - - result = create_leaf_tile(path, tgrid); - - /* Encapsulation */ - - switch (endpoint) - { - case 'N': - case 'n': - new = create_inter_tile((*tile)->parent, false, result, *tile); - break; - - case 'E': - case 'e': - new = create_inter_tile((*tile)->parent, true, *tile, result); - break; - - case 'S': - case 's': - new = create_inter_tile((*tile)->parent, false, *tile, result); - break; - - case 'W': - case 'w': - new = create_inter_tile((*tile)->parent, true, result, *tile); - break; - - default: - assert(false); - new = NULL; - break; - - } - - /* Connexions */ - - *tile = new; - - result->parent = new; - - if (container != NULL) - { - g_object_ref(G_OBJECT(new->widget)); - gtk_container_add(GTK_CONTAINER(container), new->widget); - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : tile = tuile parente, prochaine victime de promotion. * -* side = côté de tuile amené à disparaître. * -* * -* Description : Retire une moitié de tuile vide au plein profit de l'autre. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void collapse_tile(grid_tile_t *tile, grid_tile_t *side) -{ - grid_tile_t *promoted; /* Tuile à faire remonter */ - GtkWidget *container; /* Conteneur à vider */ - - assert(!IS_LEAF_TILE(tile)); - - /* Sélection du remplaçant */ - - if (side == tile->children[0]) - promoted = tile->children[1]; - else - promoted = tile->children[0]; - - /* Etablissement d'une place nette */ - - gtk_container_remove(GTK_CONTAINER(tile->widget), promoted->widget); - - container = gtk_widget_get_parent(tile->widget); - gtk_container_remove(GTK_CONTAINER(container), tile->widget); - - delete_tile(side); - - /* Promotion effective */ - - tile->widget = promoted->widget; - - tile->path = promoted->path; - - tile->children[0] = promoted->children[0]; - tile->children[1] = promoted->children[1]; - - g_object_ref(G_OBJECT(promoted->widget)); - gtk_container_add(GTK_CONTAINER(container), tile->widget); - - free(promoted); - -} - - -/****************************************************************************** -* * -* Paramètres : tile = point de départ des recherches locales. * -* widget = composant graphique à retrouver. * -* * -* Description : Tente de mettre la main sur une station d'accueil. * -* * -* Retour : Eventuelle tuile trouvée ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static grid_tile_t *find_tile_for_widget(grid_tile_t *tile, GtkWidget *widget) -{ - grid_tile_t *result; /* Tuile à retourner */ - - if (IS_LEAF_TILE(tile)) - result = tile->widget == widget ? tile : NULL; - - else - { - result = find_tile_for_widget(tile->children[0], widget); - - if (result == NULL) - result = find_tile_for_widget(tile->children[1], widget); - - } - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* INTERFACE DU COMPOSANT GTK */ -/* ---------------------------------------------------------------------------------- */ - - -/* Détermine le type du conteneur d'affichage en tuiles nommées. */ -G_DEFINE_TYPE(GtkTiledGrid, gtk_tiled_grid, GTK_TYPE_BIN) - - -/****************************************************************************** -* * -* Paramètres : klass = classe GTK à initialiser. * -* * -* Description : Initialise la classe des conteneurs d'affichage en tuiles. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_tiled_grid_class_init(GtkTiledGridClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)gtk_tiled_grid_dispose; - object->finalize = (GObjectFinalizeFunc)gtk_tiled_grid_finalize; - - g_signal_new("station-created", - GTK_TYPE_TILED_GRID, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GtkTiledGridClass, station_created), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GTK_TYPE_DOCK_STATION); - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = instance GTK à initialiser. * -* * -* Description : Initialise une instance de conteneur d'affichage en tuiles. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_tiled_grid_init(GtkTiledGrid *tgrid) -{ - tgrid->tiles = NULL; - - tgrid->def_panel = NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_tiled_grid_dispose(GtkTiledGrid *tgrid) -{ - if (tgrid->tiles != NULL) - { - delete_tile(tgrid->tiles); - tgrid->tiles = NULL; - } - - g_clear_object(&tgrid->def_panel); - - G_OBJECT_CLASS(gtk_tiled_grid_parent_class)->dispose(G_OBJECT(tgrid)); - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_tiled_grid_finalize(GtkTiledGrid *tgrid) -{ - G_OBJECT_CLASS(gtk_tiled_grid_parent_class)->finalize(G_OBJECT(tgrid)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée une nouvelle instance de conteneur avec tuiles. * -* * -* Retour : Composant GTK mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GtkWidget *gtk_tiled_grid_new(void) -{ - return g_object_new(GTK_TYPE_TILED_GRID, NULL); - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = conteneur d'affichage en tuiles à consulter. * -* * -* Description : Donne le panneau fourni par défaut pour la zone principale. * -* * -* Retour : Panneau d'affichage par défault ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPanelItem *gtk_tiled_grid_get_default_main_panel(const GtkTiledGrid *tgrid) -{ - GPanelItem *result; /* Panneau à retourner */ - - result = tgrid->def_panel; - - if (result != NULL) - g_object_ref(G_OBJECT(result)); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = conteneur d'affichage en tuiles à modifier. * -* panel = panneau d'affichage par défault ou NULL. * -* * -* Description : Fournit le panneau par défaut pour la zone principale. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_tiled_grid_set_default_main_panel(GtkTiledGrid *tgrid, GPanelItem *panel) -{ - GtkWidget *widget; /* Composant GTK à retirer */ - GtkWidget *parent; /* Conteneur à vider */ - grid_tile_t *tile; /* Première tuile d'accueil */ - - if (tgrid->def_panel != NULL) - { - widget = gtk_dockable_build_widget(GTK_DOCKABLE(tgrid->def_panel)); - - parent = gtk_widget_get_parent(widget); - - if (parent != NULL) - gtk_container_remove(GTK_CONTAINER(parent), widget); - - g_object_unref(G_OBJECT(widget)); - - g_object_unref(G_OBJECT(tgrid->def_panel)); - - } - - tgrid->def_panel = panel; - - if (panel != NULL) - { - g_object_ref(G_OBJECT(panel)); - - if (tgrid->tiles == NULL) - gtk_tiled_grid_add(tgrid, panel); - - else - { - tile = find_suitable_tile(&tgrid->tiles, "M", tgrid); - - if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(tile->widget)) == 0) - gtk_tiled_grid_add(tgrid, panel); - - } - - } - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = conteneur d'affichage en tuiles à modifier. * -* panel = panneau d'affichage à intégrer. * -* * -* Description : Incorpore un nouveau panneau dans le conteneur en tuiles. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_tiled_grid_add(GtkTiledGrid *tgrid, GPanelItem *panel) -{ - char *path; /* Chemin d'accès */ - char *name; /* Nom à donner à l'onglet */ - grid_tile_t *tile; /* Tuile d'accueil */ - - path = gtk_panel_item_class_get_path(G_PANEL_ITEM_GET_CLASS(panel)); - - if (!is_valid_tile_path(path)) - { - name = gtk_dockable_get_name(GTK_DOCKABLE(panel)); - log_variadic_message(LMT_ERROR, _("Invalid path '%s' for panel '%s'"), path, name); - free(name); - } - - else - { - tile = find_suitable_tile(&tgrid->tiles, path, tgrid); - assert(tile != NULL); - - gtk_dock_station_add_dockable(GTK_DOCK_STATION(tile->widget), GTK_DOCKABLE(panel)); - - g_panel_item_set_dock_at_startup(panel, true); - - /* Si c'est la toute première fois... */ - if (gtk_widget_get_parent(tile->widget) == NULL) - { - assert(tile == tgrid->tiles); - assert(tile->path[0] == 'M' && tile->path[1] == '\0'); - g_object_ref(G_OBJECT(tile->widget)); - gtk_container_add(GTK_CONTAINER(tgrid), tile->widget); - } - - /* Si on n'a plus besoin du panneau par défaut */ - if (tgrid->def_panel != NULL && tile->path[0] == 'M' && tile->path[1] == '\0') - { - /* Si ce n'est pas le panneau qu'on vient de rajouter...*/ - if (panel != tgrid->def_panel) - { - /* Enfin : si ce panneau par défaut est réellement en place */ - if (g_panel_item_is_docked(tgrid->def_panel)) - gtk_tiled_grid_remove(tgrid, tgrid->def_panel); - - } - - } - - } - - free(path); - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = conteneur d'affichage en tuiles à modifier. * -* panel = panneau d'affichage à supprimer. * -* * -* Description : Retire un panneau dans le conteneur en tuiles. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_tiled_grid_remove(GtkTiledGrid *tgrid, GPanelItem *panel) -{ - GtkWidget *station; /* Support courant */ - grid_tile_t *tile; /* Tuile d'accueil */ - - assert(g_panel_item_is_docked(panel)); - - gtk_dockable_decompose(GTK_DOCKABLE(panel), &station); - - tile = find_tile_for_widget(tgrid->tiles, station); - assert(tile != NULL); - - gtk_dock_station_remove_dockable(GTK_DOCK_STATION(station), GTK_DOCKABLE(panel)); - - g_panel_item_set_dock_at_startup(panel, false); - - if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(station)) == 0) - { - /* Si le panneau par défaut devient nécessaire */ - if (tgrid->def_panel != NULL && tile->path[0] == 'M' && tile->path[1] == '\0') - gtk_tiled_grid_add(tgrid, tgrid->def_panel); - - else - { - /* La racine est concernée ! */ - if (tile->parent == NULL) - { - assert(tile == tgrid->tiles); - - g_object_ref(G_OBJECT(tile->widget)); - gtk_container_remove(GTK_CONTAINER(tgrid), tile->widget); - - delete_tile(tile); - tgrid->tiles = NULL; - - } - - else - collapse_tile(tile->parent, tile); - - } - - } - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = conteneur d'affichage en tuiles à consulter. * -* station = station d'accueil à retrouver. * -* * -* Description : Indique le chemin correspondant à une station intégrée. * -* * -* Retour : Copie de chemin trouvé, à libérer ensuite, ou NULL si échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -char *gtk_tiled_grid_get_path_for_station(const GtkTiledGrid *tgrid, GtkDockStation *station) -{ - char *result; /* Chemin d'accès à renvoyer */ - grid_tile_t *tile; /* Tuile d'accueil */ - - tile = find_tile_for_widget(tgrid->tiles, GTK_WIDGET(station)); - - if (tile == NULL) - result = NULL; - - else - result = strdup(tile->path); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = conteneur d'affichage en tuiles à mettre à jour. * -* config = configuration à consulter. * -* * -* Description : Replace les positions des séparateurs de tuiles. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_tiled_grid_restore_positions(const GtkTiledGrid *tgrid, GGenConfig *config) -{ - - void visit_tiles_for_restoring(grid_tile_t *tile, const char *vpath) - { - GtkOrientation orientation; /* Direction de la tuile */ - char hint; /* Inutile donc indispensable */ - char *key; /* Clef d'accès à un paramètre */ - gint position; /* Nouvelle position de barre */ - size_t i; /* Boucle de parcours */ - char *child_key; /* Clef d'accès des suivants */ - - if (!IS_LEAF_TILE(tile)) - { - orientation = gtk_orientable_get_orientation(GTK_ORIENTABLE(tile->widget)); - - hint = orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v'; - - asprintf(&key, "%s%c", vpath, hint); - - if (g_generic_config_get_value(config, key, &position)) - gtk_paned_set_position(GTK_PANED(tile->widget), position); - - for (i = 0; i < 2; i++) - { - asprintf(&child_key, "%s%zu", key, i); - - visit_tiles_for_restoring(tile->children[i], child_key); - - free(child_key); - - } - - free(key); - - } - - } - - - visit_tiles_for_restoring(tgrid->tiles, "gui.panels.positions.R"); - -} - - -/****************************************************************************** -* * -* Paramètres : tgrid = conteneur d'affichage en tuiles à consulter. * -* config = configuration à mettre à jour. * -* * -* Description : Sauvegarde les positions des séparateurs de tuiles. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_tiled_grid_save_positions(const GtkTiledGrid *tgrid, GGenConfig *config) -{ - - void visit_tiles_for_saving(grid_tile_t *tile, const char *vpath) - { - GtkOrientation orientation; /* Direction de la tuile */ - char hint; /* Inutile donc indispensable */ - char *key; /* Clef d'accès à un paramètre */ - gint position; /* Nouvelle position de barre */ - size_t i; /* Boucle de parcours */ - char *child_key; /* Clef d'accès des suivants */ - - if (!IS_LEAF_TILE(tile)) - { - orientation = gtk_orientable_get_orientation(GTK_ORIENTABLE(tile->widget)); - - hint = orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v'; - - asprintf(&key, "%s%c", vpath, hint); - - position = gtk_paned_get_position(GTK_PANED(tile->widget)); - g_generic_config_create_or_udpdate_param(config, key, CPT_INTEGER, -1, position); - - for (i = 0; i < 2; i++) - { - asprintf(&child_key, "%s%zu", key, i); - - visit_tiles_for_saving(tile->children[i], child_key); - - free(child_key); - - } - - free(key); - - } - - } - - - visit_tiles_for_saving(tgrid->tiles, "gui.panels.positions.R"); - -} diff --git a/src/gtkext/tweak-int.h b/src/gtkext/tweak-int.h new file mode 100644 index 0000000..0d2c213 --- /dev/null +++ b/src/gtkext/tweak-int.h @@ -0,0 +1,65 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweak-int.h - définitions internes pour une section d'éléments à paramétrer + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GTKEXT_TWEAK_INT_H +#define _GTKEXT_TWEAK_INT_H + + +#include "tweak.h" + + + +/* Section de paramétrage pour liste d'éléments à configurer (instance) */ +struct _GtkTweakSection +{ + GtkListBoxRow parent; /* A laisser en premier */ + + GtkImage *icon; /* Eventuelle image */ + GtkLabel *label; /* Etiquette associée */ + GtkWidget *next; /* Eventuelle progression */ + + char *category; /* Groupe de rassemblement */ + + union + { + GType panel; /* Accès à la page de config. */ + char *sub; /* Sous-ensemble à presenter */ + }; + bool has_sub_section; /* Choix du champ valide */ + +}; + +/* Section de paramétrage pour liste d'éléments à configurer (classe) */ +struct _GtkTweakSectionClass +{ + GtkListBoxRowClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une nouvelle section de configuration. */ +bool gtk_tweak_section_create(GtkTweakSection *, const tweak_info_t *); + + + +#endif /* _GTKEXT_TWEAK_INT_H */ diff --git a/src/gtkext/tweak.c b/src/gtkext/tweak.c new file mode 100644 index 0000000..b03cf17 --- /dev/null +++ b/src/gtkext/tweak.c @@ -0,0 +1,319 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweak.c - section d'éléments à paramétrer + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "tweak.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include "helpers.h" +#include "tweak-int.h" + + + +/* Initialise la classe des sections d'éléments paramétrables. */ +static void gtk_tweak_section_class_init(GtkTweakSectionClass *); + +/* Initialise une instance de section d'éléments paramétrables. */ +static void gtk_tweak_section_init(GtkTweakSection *); + +/* Supprime toutes les références externes. */ +static void gtk_tweak_section_dispose(GtkTweakSection *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_tweak_section_finalize(GtkTweakSection *); + + + +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkTweakSection, gtk_tweak_section, GTK_TYPE_LIST_BOX_ROW); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des sections d'éléments paramétrables. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tweak_section_class_init(GtkTweakSectionClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)gtk_tweak_section_dispose; + object->finalize = (GObjectFinalizeFunc)gtk_tweak_section_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/tweak.ui"); + + gtk_widget_class_bind_template_child(widget, GtkTweakSection, icon); + gtk_widget_class_bind_template_child(widget, GtkTweakSection, label); + gtk_widget_class_bind_template_child(widget, GtkTweakSection, next); + +} + + +/****************************************************************************** +* * +* Paramètres : section = composant GTK à initialiser. * +* * +* Description : Initialise une instance de section d'éléments paramétrables. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tweak_section_init(GtkTweakSection *section) +{ + gtk_widget_init_template(GTK_WIDGET(section)); + + section->category = NULL; + + section->sub = NULL; + section->has_sub_section = true; + +} + + +/****************************************************************************** +* * +* Paramètres : section = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tweak_section_dispose(GtkTweakSection *section) +{ + gtk_widget_dispose_template(GTK_WIDGET(section), GTK_TYPE_TWEAK_SECTION); + + G_OBJECT_CLASS(gtk_tweak_section_parent_class)->dispose(G_OBJECT(section)); + +} + + +/****************************************************************************** +* * +* Paramètres : section = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tweak_section_finalize(GtkTweakSection *section) +{ + if (section->category != NULL) + free(section->category); + + if (section->has_sub_section && section->sub != NULL) + free(section->sub); + + G_OBJECT_CLASS(gtk_tweak_section_parent_class)->finalize(G_OBJECT(section)); + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations associées à la section. * +* * +* Description : Crée une nouvelle section de configuration. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkTweakSection *gtk_tweak_section_new(const tweak_info_t *info) +{ + GtkTweakSection *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_TWEAK_SECTION, NULL); + + if (!gtk_tweak_section_create(result, info)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à initialiser pleinement. * +* info = informations associées à la section. * +* * +* Description : Met en place une nouvelle section de configuration. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_tweak_section_create(GtkTweakSection *section, const tweak_info_t *info) +{ + bool result; /* Bilan à retourner */ + + result = true; + + gtk_image_set_from_icon_name(section->icon, info->image); + gtk_label_set_label(section->label, info->label); + + gtk_widget_set_visible(section->next, info->has_sub_section); + + section->category = strdup(info->category); + + if (info->has_sub_section) + { + section->sub = strdup(info->sub); + section->has_sub_section = true; + } + else + { + section->panel = info->panel; + section->has_sub_section = false; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à consulter. * +* * +* Description : Fournit l'étiquette associée à une section de configuration. * +* * +* Retour : Désignation humaine de la section. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *gtk_tweak_section_get_label(const GtkTweakSection *section) +{ + const char *result; /* Texte à retourner */ + + result = gtk_label_get_text(section->label); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à consulter. * +* * +* Description : Indique si la section renvoie vers une sous-section. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_tweak_section_has_sub_section(const GtkTweakSection *section) +{ + bool result; /* Statut à retourner */ + + result = section->has_sub_section; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à consulter. * +* * +* Description : Fournit le type d'un éventuel panneau de configuration lié. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GType gtk_tweak_section_get_panel(const GtkTweakSection *section) +{ + GType result; /* Type d'objet à retourner */ + + assert(!section->has_sub_section); + + result = section->panel; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à consulter. * +* * +* Description : Fournit la désignation d'une éventuelle sous-section liée. * +* * +* Retour : Désignation associée à la sous-section. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *gtk_tweak_section_get_sub_section(const GtkTweakSection *section) +{ + const char *result; /* Désignation à renvoyer */ + + assert(section->has_sub_section); + + result = section->sub; + + return result; + +} diff --git a/src/gtkext/tweak.h b/src/gtkext/tweak.h new file mode 100644 index 0000000..8c44844 --- /dev/null +++ b/src/gtkext/tweak.h @@ -0,0 +1,90 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweak.h - prototypes pour pour une section d'éléments à paramétrer + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GTKEXT_TWEAK_H +#define _GTKEXT_TWEAK_H + + +#include <stdbool.h> +#include <gtk/gtk.h> + + +#include "../glibext/helpers.h" + + + +/* Définition d'une section de configuration */ +typedef struct _tweak_info_t +{ + const char *parent; /* Ensemble d'appartenance */ + + const char *category; /* Groupe de rassemblement */ + + const char *image; /* Eventuelle image associée */ + const char *key; /* Désignation de la section */ + const char *label; /* Désignation humaine */ + + union + { + GType panel; /* Accès à la page de config. */ + const char *sub; /* Sous-ensemble à presenter */ + }; + bool has_sub_section; /* Choix du champ valide */ + +} tweak_info_t; + + +#define TWEAK_SIMPLE_DEF(p, c, i, k, l, t) \ + { \ + .parent = p, \ + .category = c, \ + .image = i, \ + .key = k, \ + .label = l, \ + .panel = t, \ + .has_sub_section = false, \ + } + +#define GTK_TYPE_TWEAK_SECTION (gtk_tweak_section_get_type()) + +DECLARE_GTYPE(GtkTweakSection, gtk_tweak_section, GTK, TWEAK_SECTION); + + +/* Crée une nouvelle section de configuration. */ +GtkTweakSection *gtk_tweak_section_new(const tweak_info_t *); + +/* Fournit l'étiquette associée à une section de configuration. */ +const char *gtk_tweak_section_get_label(const GtkTweakSection *); + +/* Indique si la section renvoie vers une sous-section. */ +bool gtk_tweak_section_has_sub_section(const GtkTweakSection *); + +/* Fournit le type d'un éventuel panneau de configuration lié. */ +GType gtk_tweak_section_get_panel(const GtkTweakSection *); + +/* Fournit la désignation d'une éventuelle sous-section liée. */ +const char *gtk_tweak_section_get_sub_section(const GtkTweakSection *); + + + +#endif /* _GTKEXT_TWEAK_H */ diff --git a/src/gtkext/tweak.ui b/src/gtkext/tweak.ui new file mode 100644 index 0000000..576e25e --- /dev/null +++ b/src/gtkext/tweak.ui @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="GtkTweakSection" parent="GtkListBoxRow"> + + <property name="child"> + + <object class="GtkBox"> + <property name="orientation">horizontal</property> + <property name="margin-top">14</property> + <property name="margin-bottom">14</property> + + <child> + <object class="GtkImage" id="icon"> + <property name="icon-name">security-high-symbolic</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <style> + <class name="icon-dropshadow"/> + </style> + </object> + </child> + + <child> + <object class="GtkLabel" id="label"> + <property name="xalign">0</property> + <property name="label" translatable="yes">Security</property> + <property name="hexpand">true</property> + </object> + </child> + + <child> + <object class="GtkImage" id="next"> + <property name="icon-name">go-next-symbolic</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <style> + <class name="icon-dropshadow"/> + </style> + </object> + </child> + + </object> + </property> + + </template> +</interface> diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index f4a682c..be70445 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -12,15 +12,12 @@ libgui_la_SOURCES = \ item-int.h \ item.h item.c \ menubar.h menubar.c \ - panel-int.h \ - panel.h panel.c \ resources.h resources.c \ status.h status.c \ theme.h theme.c libgui_la_LIBADD = \ core/libguicore.la \ - dialogs/libguidialogs.la \ menus/libguimenus.la \ panels/libguipanels.la \ tb/libguitb.la @@ -28,10 +25,15 @@ libgui_la_LIBADD = \ libgui_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) -libgui4_la_SOURCES = +libgui4_la_SOURCES = \ + resources.h resources.c \ + window-int.h \ + window.h window.c -libgui4_la_LIBADD = \ - core/libguicore4.la +libgui4_la_LIBADD = \ + core/libguicore4.la \ + dialogs/libguidialogs.la \ + panels/libguipanels4.la libgui4_la_CFLAGS = $(LIBGTK4_CFLAGS) @@ -41,10 +43,14 @@ devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) dev_HEADERS = $(libgui_la_SOURCES:%c=) -SUBDIRS = core # dialogs menus panels tb +SUBDIRS = core dialogs panels # menus panels tb -resources.c: gresource.xml $(UI_FILES) +RES_FILES = \ + style.css \ + window.ui + +resources.c: gresource.xml $(RES_FILES) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui gresource.xml resources.h: gresource.xml @@ -53,4 +59,4 @@ resources.h: gresource.xml CLEANFILES = resources.h resources.c -EXTRA_DIST = gresource.xml $(UI_FILES) +EXTRA_DIST = gresource.xml $(RES_FILES) diff --git a/src/gui/core/Makefile.am b/src/gui/core/Makefile.am index 96ef578..a854977 100644 --- a/src/gui/core/Makefile.am +++ b/src/gui/core/Makefile.am @@ -15,7 +15,6 @@ RES_FILES = \ libguicore_la_SOURCES = \ global.h global.c \ items.h items.c \ - panels.h panels.c \ resources.h resources.c \ theme.h theme.c @@ -24,7 +23,9 @@ libguicore_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) libguicore4_la_SOURCES = \ core.h core.c \ - logs.h logs.c + logs.h logs.c \ + nox.h nox.c \ + panels.h panels.c libguicore4_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) diff --git a/src/gui/core/core.c b/src/gui/core/core.c index 2d47dc9..57a398a 100644 --- a/src/gui/core/core.c +++ b/src/gui/core/core.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.c - chargement et le déchargement du tronc commun pour l'éditeur graphique * - * Copyright (C) 2016-2019 Cyrille Bagard + * Copyright (C) 2016-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -24,6 +24,7 @@ #include "core.h" +#include "panels.h" #include "../../glibext/linetoken.h" @@ -32,6 +33,7 @@ static AvailableGuiComponent __loaded = AGC_NONE; + /****************************************************************************** * * * Paramètres : flags = liste d'éléments à charger. * @@ -59,6 +61,15 @@ bool load_gui_components(AvailableGuiComponent flags) } + if ((flags & AGC_PANELS) != 0 && (__loaded & AGC_PANELS) == 0) + { + result = load_main_framework_panel_definitions(); + if (!result) goto done; + + __loaded |= AGC_PANELS; + + } + done: return result; @@ -80,6 +91,14 @@ bool load_gui_components(AvailableGuiComponent flags) void unload_gui_components(AvailableGuiComponent flags) { + if ((flags & AGC_PANELS) != 0 && (__loaded & AGC_PANELS) == 0) + { + unload_all_framework_panel_definitions(); + + __loaded &= ~AGC_PANELS; + + } + if ((flags & AGC_BUFFER_FEATURES) != 0 && (__loaded & AGC_BUFFER_FEATURES) == 0) { exit_segment_content_hash_table(); @@ -87,7 +106,6 @@ void unload_gui_components(AvailableGuiComponent flags) __loaded &= ~AGC_BUFFER_FEATURES; } - } diff --git a/src/gui/core/core.h b/src/gui/core/core.h index bcb5433..19647ce 100644 --- a/src/gui/core/core.h +++ b/src/gui/core/core.h @@ -34,6 +34,7 @@ typedef enum _AvailableGuiComponent { AGC_NONE = (0 << 0), /* Statut initial */ AGC_BUFFER_FEATURES = (1 << 0), /* Tampons de bribes de texte */ + AGC_PANELS = (1 << 1), /* Panneaux graphiques de base */ } AvailableGuiComponent; diff --git a/src/gui/core/logs.c b/src/gui/core/logs.c index 59910f1..cdb2a0d 100644 --- a/src/gui/core/logs.c +++ b/src/gui/core/logs.c @@ -24,6 +24,14 @@ #include "logs.h" +#include <assert.h> + + +#include "panels.h" +#include "../panels/logs.h" +#include "../../glibext/log.h" + + /****************************************************************************** * * @@ -38,26 +46,20 @@ * * ******************************************************************************/ -void do_log_message_alt2(LogMessageType type, const char *msg) +void do_log_message_alt(LogMessageType type, const char *msg) { -#if 0 - -#ifdef INCLUDE_GTK_SUPPORT - - GEditorItem *item; /* Eventuel affichage présent */ + GLogEntry *entry; /* Nouvel élément de journal */ + GtkTiledPanel *panel; /* Panneau de journalisation */ - item = find_editor_item_by_type(G_TYPE_LOG_PANEL); + entry = g_log_entry_new(type, msg); - if (item != NULL) - { - g_log_panel_add_message(G_LOG_PANEL(item), type, msg); - g_object_unref(G_OBJECT(item)); - } + panel = get_framework_panel_singleton(GTK_TYPE_LOGS_PANEL); + assert(panel != NULL); -#endif + g_log_panel_add_message(GTK_LOGS_PANEL(panel), entry); -#endif + unref_object(panel); - printf("[log GUI] [%u] %s\n", type, msg); + unref_object(entry); } diff --git a/src/gui/core/nox.c b/src/gui/core/nox.c new file mode 100644 index 0000000..7f3ae1f --- /dev/null +++ b/src/gui/core/nox.c @@ -0,0 +1,48 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * nox.c - indication de présence ou d'absence de support graphique + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "nox.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Indique la présence ou l'absence d'un affichage graphique. * +* * +* Retour : Statut à transmettre. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool _run_in_nox_mode(void) +{ + bool result; /* Statut à retournr */ + + result = false; + + return result; + +} diff --git a/src/gui/core/nox.h b/src/gui/core/nox.h new file mode 100644 index 0000000..e03b365 --- /dev/null +++ b/src/gui/core/nox.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * nox.h - prototypes pour l'indication de présence ou d'absence de support graphique + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_CORE_NOX_H +#define _GUI_CORE_NOX_H + + +#include <stdbool.h> + + + +/* Indique la présence ou l'absence d'un affichage graphique. */ +bool _run_in_nox_mode(void); + + + +#endif /* _GUI_CORE_NOX_H */ diff --git a/src/gui/core/panels.c b/src/gui/core/panels.c index 1b6f604..9fca411 100644 --- a/src/gui/core/panels.c +++ b/src/gui/core/panels.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * panels.c - gestion d'ensemble de tous les panneaux pour l'éditeur + * panels.c - gestion d'ensemble de tous les panneaux graphiques du framework * - * Copyright (C) 2016-2019 Cyrille Bagard + * Copyright (C) 2016-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -27,35 +27,100 @@ #include <assert.h> #include <malloc.h> +#include <string.h> -#include "items.h" -#include "../panel-int.h" -#include "../panels/bintree.h" -#include "../panels/bookmarks.h" -#include "../panels/errors.h" -#include "../panels/glance.h" -#include "../panels/history.h" -#include "../panels/log.h" -#include "../panels/regedit.h" -#include "../panels/strings.h" -#include "../panels/symbols.h" +#include <i18n.h> + + +#include "../panels/binary.h" +#include "../panels/binary-params.h" +#include "../panels/logs.h" #include "../panels/welcome.h" -#include "../../core/params.h" +#include "../../gtkext/launcher.h" + + + +/* Définition générique complète d'un panneau */ +typedef struct _ext_panel_info_t +{ + /* Début des champs copiés de panel_info_t */ + + char *category; /* Groupe de rassemblement */ + + char *image; /* Eventuelle image associée */ + char *title; /* Désignation humaine */ + char *desc; /* Description humaine */ + + FrameworkPanelPersonality personality; /* Comportement attendu */ + + GType panel_type; /* Type du panneau représenté */ + GType params_type; /* Composant de paramètre */ + + /* Fin des champs copiés de panel_info_t */ + + GtkTiledPanel *singleton; /* Conservation des allocations*/ +} ext_panel_info_t; /* Liste des panneaux disponibles */ -static GType *_panels_list = NULL; +static ext_panel_info_t **_panels_list = NULL; static size_t _panels_count = 0; +/* Copie une définition basique de panneau graphqiue. */ +static ext_panel_info_t *copy_panel_info(const panel_info_t *); + +/* Efface une définition étendue de panneau graphique. */ +static void delete_panel_info(ext_panel_info_t *); + + /****************************************************************************** * * -* Paramètres : - * +* Paramètres : info = information de base à copier. * +* * +* Description : Copie une définition basique de panneau graphqiue. * +* * +* Retour : Structure mémorisant l'ensemble des informations. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static ext_panel_info_t *copy_panel_info(const panel_info_t *info) +{ + ext_panel_info_t *result; /* Structure à retourner */ + + result = calloc(1, sizeof(ext_panel_info_t)); + + if (info->category != NULL) + result->category = strdup(info->category); + + if (info->image != NULL) + result->image = strdup(info->image); + + result->title = strdup(info->title); + + if (info->desc != NULL) + result->desc = strdup(info->desc); + + result->personality = info->personality; + + result->panel_type = info->panel_type; + result->params_type = info->params_type; + + return result; + +} + + +/****************************************************************************** * * -* Description : Charge les principaux panneaux de l'éditeur. * +* Paramètres : info = informations à supprimer de la mémoire. * +* * +* Description : Efface une définition étendue de panneau graphique. * * * * Retour : - * * * @@ -63,38 +128,132 @@ static size_t _panels_count = 0; * * ******************************************************************************/ -void load_main_panels(void) +static void delete_panel_info(ext_panel_info_t *info) { - GGenConfig *config; /* Configuration globale */ - GPanelItem *panel; /* Panneau à précharger */ + if (info->category != NULL) + free(info->category); + + if (info->image != NULL) + free(info->image); + + free(info->title); + + if (info->desc != NULL) + free(info->desc); + + if (info->singleton != NULL) + { + assert(info->personality & FPP_SINGLETON); + unref_object(info->singleton); + } + + free(info); + +} - config = get_main_configuration(); - register_panel_item(G_TYPE_LOG_PANEL, config); +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Charge les définitions des principaux panneaux du framework. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool load_main_framework_panel_definitions(void) +{ + bool result; /* Bilan à retourner */ + panel_info_t info; /* Infos d'enregistrement */ /* Chargement du panneau de rapport au plus tôt */ - panel = g_panel_item_new(G_TYPE_LOG_PANEL, NULL); - g_object_unref(G_OBJECT(panel)); - - register_panel_item(G_TYPE_WELCOME_PANEL, config); - register_panel_item(G_TYPE_REGEDIT_PANEL, config); - register_panel_item(G_TYPE_SYMBOLS_PANEL, config); - register_panel_item(G_TYPE_HISTORY_PANEL, config); - register_panel_item(G_TYPE_STRINGS_PANEL, config); - register_panel_item(G_TYPE_GLANCE_PANEL, config); - register_panel_item(G_TYPE_BOOKMARKS_PANEL, config); - register_panel_item(G_TYPE_BINTREE_PANEL, config); - register_panel_item(G_TYPE_ERROR_PANEL, config); + + info.category = NULL; + + info.image = NULL; + info.title = _("Logs"); + info.desc = NULL; + + info.personality = FPP_SINGLETON; + + info.panel_type = GTK_TYPE_LOGS_PANEL; + info.params_type = G_TYPE_INVALID; + + result = register_framework_panel_definition(&info); + if (!result) goto done; + + /* Chargements des panneaux restants */ + + info.category = "Main"; + + info.image = "binfile-symbolic"; + info.title = _("Binary analysis"); + info.desc = _("Load a binary content and parse its format if recognized"); + + info.personality = FPP_MAIN_PANEL; + + info.panel_type = GTK_TYPE_BINARY_PANEL; + info.params_type = GTK_TYPE_BINARY_PARAMETERS; + + result = register_framework_panel_definition(&info); + if (!result) goto done; + + /* --- */ + + info.category = NULL; + + info.image = NULL; + info.title = _("Welcome"); + info.desc = NULL; + + info.personality = FPP_MAIN_PANEL | FPP_SINGLETON; + + info.panel_type = GTK_TYPE_WELCOME_PANEL; + info.params_type = G_TYPE_INVALID; + + result = register_framework_panel_definition(&info); + if (!result) goto done; + + done: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Décharge tous les panneaux graphiques du framework. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void unload_all_framework_panel_definitions(void) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < _panels_count; i++) + delete_panel_info(_panels_list[i]); + + _panels_list = NULL; + _panels_count = 0; } /****************************************************************************** * * -* Paramètres : type = type du composant à présenter à l'affichage. * -* config = configuration à compléter. * +* Paramètres : info = information de base à copier. * * * -* Description : Enregistre un panneau comme partie intégrante de l'éditeur. * +* Description : Enregistre la définition d'un panneau graphique. * * * * Retour : - * * * @@ -102,71 +261,199 @@ void load_main_panels(void) * * ******************************************************************************/ -void register_panel_item(GType type, GGenConfig *config) +bool register_framework_panel_definition(const panel_info_t *info) { - GPanelItemClass *class; /* Classe associée au type */ -#ifndef NDEBUG - bool status; /* Bilan de mise en place */ -#endif + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + ext_panel_info_t *ext_info; /* Informations conservées */ + + result = false; + + /* Validation */ + + for (i = 0; i < _panels_count; i++) + if (_panels_list[i]->panel_type == info->panel_type) + break; - _panels_list = realloc(_panels_list, ++_panels_count * sizeof(GType)); + if (i < _panels_count) + goto done; - _panels_list[_panels_count - 1] = type; + /* Enregistrement */ - class = g_type_class_ref(type); + ext_info = copy_panel_info(info); -#ifndef NDEBUG - status = gtk_panel_item_class_setup_configuration(class, config); - assert(status); -#else - gtk_panel_item_class_setup_configuration(class, config); -#endif + _panels_list = realloc(_panels_list, ++_panels_count * sizeof(ext_panel_info_t *)); - g_type_class_unref(class); + _panels_list[_panels_count - 1] = ext_info; + + result = true; + + done: + + return result; } /****************************************************************************** * * -* Paramètres : skip = saute le panneau d'accueil lors du parcours ? * -* handle = routine à appeler pour chaque panneau. * -* data = données fournies pour accompagner cet appel. * +* Paramètres : target = type de définition de panneau recherchée. * * * -* Description : Effectue le parcours de tous les panneaux chargés. * +* Description : Récupère les particularités d'un panneau graphique. * * * -* Retour : true si le parcours a été total, false sinon. * +* Retour : Détails du comportement associé au panneau visé. * * * * Remarques : - * * * ******************************************************************************/ -bool _browse_all_item_panels(bool skip, handle_panel_item_fc handle, void *data) +FrameworkPanelPersonality get_framework_panel_personality(GType target) { - bool result; /* Résultat à renvoyer */ - GType type; /* Type de panneau à traiter */ + FrameworkPanelPersonality result; /* Propriétées à retourner */ size_t i; /* Boucle de parcours */ - GPanelItemClass *class; /* Classe associée au type */ + ext_panel_info_t *info; /* Informations conservées */ - result = true; + result = FPP_NONE; + + for (i = 0; i < _panels_count; i++) + { + info = _panels_list[i]; + + if (info->panel_type == target) + { + result = info->personality; + break; + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : target = type de définition de panneau recherchée. * +* * +* Description : Met en place (au besoin) un panneau graphique unique. * +* * +* Retour : Instance de définition identifiée ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkTiledPanel *get_framework_panel_singleton(GType target) +{ + GtkTiledPanel *result; /* Instance à renvoyer */ + size_t i; /* Boucle de parcours */ + ext_panel_info_t *info; /* Informations conservées */ + + result = NULL; + + for (i = 0; i < _panels_count; i++) + { + info = _panels_list[i]; + + if (info->panel_type == target) + { + if (info->singleton == NULL) + info->singleton = g_object_new(target, NULL); + + result = info->singleton; + ref_object(result); + break; + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : list = liste à compléter. * +* * +* Description : Intègre une définition de panneau enregistrée. * +* * +* Retour : true pour un parcours complet de la liste des définitions. * +* * +* Remarques : - * +* * +******************************************************************************/ + +void populate_framework_panel_launcher_list(GtkListBox *list) +{ + size_t i; /* Boucle de parcours */ + ext_panel_info_t *info; /* Informations conservées */ + GtkPanelLauncher *launcher; /* Lanceur à intégrer */ for (i = 0; i < _panels_count; i++) { - type = _panels_list[i]; + info = _panels_list[i]; - if (skip && type == G_TYPE_WELCOME_PANEL) + if (info->category == NULL) continue; - class = g_type_class_ref(type); + launcher = gtk_panel_launcher_new(info->image, info->title, info->desc); + + g_object_set_data(G_OBJECT(launcher), "panel_type", GSIZE_TO_POINTER(info->panel_type)); + + gtk_list_box_append(list, GTK_WIDGET(launcher)); - result = handle(class, data); + } + +} - g_type_class_unref(class); - if (!result) break; +/****************************************************************************** +* * +* Paramètres : row = lanceur sélectionné. * +* * +* Description : Fournit un composant d'édition de paramètres de panneau. * +* * +* Retour : Composant d'édition de paramètres ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkWidget *get_framework_panel_parameters(GtkListBoxRow *row) +{ + GtkWidget *result; /* Composant à retourner */ + gpointer *data; /* Valeur incrustée */ + GType target; /* Type de panneau recherché */ + ext_panel_info_t *info; /* Informations conservées */ + size_t i; /* Boucle de parcours */ + + data = g_object_get_data(G_OBJECT(row), "panel_type"); + assert(data != NULL); + + target = GPOINTER_TO_SIZE(data); + + info = NULL; + + for (i = 0; i < _panels_count; i++) + { + info = _panels_list[i]; + + if (info->panel_type == target) + break; } + assert(info != NULL); + assert(i < _panels_count); + + if (info->params_type == G_TYPE_INVALID) + result = NULL; + else + result = g_object_new(info->params_type, NULL); + return result; } diff --git a/src/gui/core/panels.h b/src/gui/core/panels.h index 3846038..e17ef8a 100644 --- a/src/gui/core/panels.h +++ b/src/gui/core/panels.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * panels.h - prototypes pour la gestion d'ensemble de tous les panneaux pour l'éditeur + * panels.h - prototypes pour la gestion d'ensemble de tous les panneaux graphiques du framework * - * Copyright (C) 2016-2019 Cyrille Bagard + * Copyright (C) 2016-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,28 +26,62 @@ #define _GUI_CORE_PANELS_H +#include <glib-object.h> #include <stdbool.h> +#include <gtk/gtk.h> -#include "../panel.h" -#include "../../glibext/configuration.h" +#include "../../gtkext/panel.h" -/* Charge les principaux panneaux de l'éditeur. */ -void load_main_panels(void); +/* Types de panneaux pour éditeur */ +typedef enum _FrameworkPanelPersonality +{ + FPP_NONE = (0 << 0), /* Pas de particularité */ -/* Enregistre un panneau comme partie intégrante de l'éditeur. */ -void register_panel_item(GType, GGenConfig *); + FPP_MAIN_PANEL = (1 << 0), /* Panneau principal */ + FPP_SINGLETON = (1 << 1), /* Instance unique */ -/* Réalise un traitement sur un panneau de l'éditeur. */ -typedef bool (* handle_panel_item_fc) (GPanelItemClass *, void *); +} FrameworkPanelPersonality; -/* Effectue le parcours de tous les panneaux chargés. */ -bool _browse_all_item_panels(bool, handle_panel_item_fc, void *); +/* Définition générique d'un panneau */ +typedef struct _panel_info_t +{ + const char *category; /* Groupe de rassemblement */ -#define browse_all_item_panels(h, d) \ - _browse_all_item_panels(false, h, d) + const char *image; /* Eventuelle image associée */ + const char *title; /* Désignation humaine */ + const char *desc; /* Description humaine */ + + FrameworkPanelPersonality personality; /* Comportement attendu */ + + GType panel_type; /* Type du panneau représenté */ + GType params_type; /* Composant de paramètre */ + +} panel_info_t; + + +/* Charge les définitions des principaux panneaux du framework. */ +bool load_main_framework_panel_definitions(void); + +/* Décharge tous les panneaux graphiques du framework. */ +void unload_all_framework_panel_definitions(void); + +/* Enregistre la définition d'un panneau graphique. */ +bool register_framework_panel_definition(const panel_info_t *); + +/* Récupère les particularités d'un panneau graphique. */ +FrameworkPanelPersonality get_framework_panel_personality(GType); + +/* Met en place (au besoin) un panneau graphique unique. */ +GtkTiledPanel *get_framework_panel_singleton(GType); + +/* Intègre une définition de panneau enregistrée. */ +void populate_framework_panel_launcher_list(GtkListBox *); + +/* Fournit un composant d'édition de paramètres de panneau. */ +GtkWidget *get_framework_panel_parameters(GtkListBoxRow *); diff --git a/src/gui/dialogs/Makefile.am b/src/gui/dialogs/Makefile.am index 5716f14..3492f63 100644 --- a/src/gui/dialogs/Makefile.am +++ b/src/gui/dialogs/Makefile.am @@ -3,35 +3,43 @@ BUILT_SOURCES = resources.h resources.c noinst_LTLIBRARIES = libguidialogs.la -UI_FILES = \ - about.ui \ - bookmark.ui \ - export_graph.ui \ - identity.ui \ - loading.ui \ - preferences.ui \ - prefs_fgraph.ui \ - prefs_labels.ui \ - snapshots.ui \ - storage.ui - -libguidialogs_la_SOURCES = \ - about.h about.c \ - bookmark.h bookmark.c \ - export_disass.h export_disass.c \ - export_graph.h export_graph.c \ - goto.h goto.c \ - gotox.h gotox.c \ - identity.h identity.c \ - loading.h loading.c \ - preferences.h preferences.c \ - prefs_fgraph.h prefs_fgraph.c \ - prefs_labels.h prefs_labels.c \ - resources.h resources.c \ - snapshots.h snapshots.c \ - storage.h storage.c - -libguidialogs_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) + +UI_FILES = \ + about.css \ + about.ui \ + preferences.ui +# bookmark.ui \ +# export_graph.ui \ +# identity.ui \ +# loading.ui \ +# prefs_fgraph.ui \ +# prefs_labels.ui \ +# snapshots.ui \ +# storage.ui + +libguidialogs_la_SOURCES = \ + about-int.h \ + about.h about.c \ + preferences-int.h \ + preferences.h preferences.c \ + resources.h resources.c +# bookmark.h bookmark.c \ +# export_disass.h export_disass.c \ +# export_graph.h export_graph.c \ +# goto.h goto.c \ +# gotox.h gotox.c \ +# identity.h identity.c \ +# loading.h loading.c \ +# \ +# prefs_fgraph.h prefs_fgraph.c \ +# prefs_labels.h prefs_labels.c \ +# snapshots.h snapshots.c \ +# storage.h storage.c + +libguidialogs_la_LIBADD = \ + prefs/libguidialogsprefs.la + +libguidialogs_la_CFLAGS = $(LIBGTK4_CFLAGS) devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) @@ -39,6 +47,9 @@ devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) dev_HEADERS = $(libguidialogs_la_SOURCES:%c=) +SUBDIRS = prefs + + resources.c: gresource.xml $(UI_FILES) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_dialogs gresource.xml diff --git a/src/gui/dialogs/about-int.h b/src/gui/dialogs/about-int.h new file mode 100644 index 0000000..96a470e --- /dev/null +++ b/src/gui/dialogs/about-int.h @@ -0,0 +1,55 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * about-int.h - définitions internes pour la boîte de dialogue d'information sur le programme + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_DIALOGS_ABOUT_INT_H +#define _GUI_DIALOGS_ABOUT_INT_H + + +#include <stdbool.h> + + +#include "about.h" + + + +/* Boîte "A propos de" dédiée à l'application (instance) */ +struct _GtkAppAboutDialog +{ + GtkWindow parent; /* A laisser en premier */ + +}; + +/* Boîte "A propos de" dédiée à l'application (classe) */ +struct _GtkAppAboutDialogClass +{ + GtkWindowClass parent; /* A laisser en premier */ + +}; + + +/* Met en place la fenêtre d'informations sur le logiciel. */ +bool gtk_app_about_dialog_create(GtkAppAboutDialog *, GtkWindow *); + + + +#endif /* _GUI_DIALOGS_ABOUT_INT_H */ diff --git a/src/gui/dialogs/about.c b/src/gui/dialogs/about.c index 574c7f2..1dca752 100644 --- a/src/gui/dialogs/about.c +++ b/src/gui/dialogs/about.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * about.h - boîte de dialogue d'information sur le programme * - * Copyright (C) 2015-2020 Cyrille Bagard + * Copyright (C) 2015-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -33,143 +33,168 @@ #include <config.h> -#include "../../gtkext/easygtk.h" +#include "about-int.h" -/* Réagit à l'appui d'une touche sur la fenêtre 'A propos'. */ -static gboolean close_about_window_on_escape(GtkWidget *, GdkEventKey *, gpointer); +/* Procède à l'initialisation de la boîte "A propos de". */ +static void gtk_app_about_dialog_class_init(GtkAppAboutDialogClass *); -/* Dessine un fond adapté pour la fenêtre sans toucher au thème. */ -static gboolean draw_black_background(GtkWidget *, cairo_t *, gpointer); +/* Procède à l'initialisation de la boîte "A propos de". */ +static void gtk_app_about_dialog_init(GtkAppAboutDialog *); +/* Supprime toutes les références externes. */ +static void gtk_app_about_dialog_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_app_about_dialog_finalize(GObject *); + + + +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkAppAboutDialog, gtk_app_about_dialog, GTK_TYPE_WINDOW); /****************************************************************************** * * -* Paramètres : parent = fenêtre parente à surpasser. * -* outb = constructeur à détruire après usage. [OUT] * +* Paramètres : class = classe GTK à initialiser. * * * -* Description : Construit la fenêtre d'informations sur le logiciel. * +* Description : Procède à l'initialisation de la boîte "A propos de". * * * -* Retour : Adresse de la fenêtre mise en place. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GtkWidget *create_about_dialog(GtkWindow *parent, GtkBuilder **outb) +static void gtk_app_about_dialog_class_init(GtkAppAboutDialogClass *class) { - GtkWidget *result; /* Fenêtre à renvoyer */ - GtkBuilder *builder; /* Constructeur utilisé */ - unsigned int revision; /* Numéro de révision */ - unsigned int max; /* Nbre. de boucles à effectuer*/ - unsigned int i; /* Boucle de parcours */ - unsigned int level; /* Unité la plus importante */ - char buffer[64]; /* Nom d'image à forger */ - GtkImage *img; /* Composant d'affichage */ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ - builder = gtk_builder_new_from_resource("/org/chrysalide/gui/dialogs/about.ui"); - *outb = builder; + object = G_OBJECT_CLASS(class); - result = GTK_WIDGET(gtk_builder_get_object(builder, "window")); + object->dispose = gtk_app_about_dialog_dispose; + object->finalize = gtk_app_about_dialog_finalize; - gtk_window_set_transient_for(GTK_WINDOW(result), parent); + widget = GTK_WIDGET_CLASS(class); - /* Numéro de révision */ + gtk_widget_class_set_css_name(widget, "aboutdialog"); - revision = REVISION; - max = log(revision) / log(10); + gtk_widget_class_add_binding_action(widget, GDK_KEY_Escape, 0, "window.close", NULL); - assert(max <= 6); + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/about.ui"); - for (i = 0; i <= max; i++) - { - snprintf(buffer, 64, "revision_%u", i); +} - img = GTK_IMAGE(gtk_builder_get_object(builder, buffer)); - level = pow(10, max - i); +/****************************************************************************** +* * +* Paramètres : dialog = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation de la boîte "A propos de". * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - snprintf(buffer, 64, "/org/chrysalide/gui/dialogs/about/revision_%u.png", revision / level); +static void gtk_app_about_dialog_init(GtkAppAboutDialog *dialog) +{ + gtk_widget_init_template(GTK_WIDGET(dialog)); - gtk_image_set_from_resource(img, buffer); +} - revision %= level; - } +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - /* Connexion des signaux */ +static void gtk_app_about_dialog_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_APP_ABOUT_DIALOG); - gtk_builder_add_callback_symbols(builder, - BUILDER_CALLBACK(close_about_window_on_escape), - BUILDER_CALLBACK(draw_black_background), - NULL); + G_OBJECT_CLASS(gtk_app_about_dialog_parent_class)->dispose(object); - gtk_builder_connect_signals(builder, builder); +} - return result; + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_app_about_dialog_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_app_about_dialog_parent_class)->finalize(object); } /****************************************************************************** * * -* Paramètres : widget = fenêtre visée par la procédure. * -* event = informations liées à l'événement. * -* dummy = donnée non utilisée ici. * +* Paramètres : parent = fenêtre parente à surpasser. * * * -* Description : Réagit à l'appui d'une touche sur la fenêtre 'A propos'. * +* Description : Construit la fenêtre d'informations sur le logiciel. * * * -* Retour : TRUE pour interrompre la propagation, FALSE autrement. * +* Retour : Adresse de la fenêtre mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean close_about_window_on_escape(GtkWidget *widget, GdkEventKey *event, gpointer dummy) +GtkWindow *gtk_app_about_dialog_new(GtkWindow *parent) { - gboolean result; /* Ordre à retourner */ + GtkWindow *result; /* Boite de dialogue à renvoyer*/ + + result = g_object_new(GTK_TYPE_APP_ABOUT_DIALOG, NULL); - if (event->keyval == GDK_KEY_Escape) - { - gtk_widget_destroy(widget); - result = TRUE; - } - else result = FALSE; + if (!gtk_app_about_dialog_create(GTK_APP_ABOUT_DIALOG(result), parent)) + g_clear_object(&result); return result; } + /****************************************************************************** * * -* Paramètres : widget = fenêtre visée par la procédure. * -* event = informations liées à l'événement. * -* dummy = donnée non utilisée ici. * +* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * +* parent = fenêtre parente à surpasser. * * * -* Description : Dessine un fond adapté pour la fenêtre sans toucher au thème.* +* Description : Met en place la fenêtre d'informations sur le logiciel. * * * -* Retour : TRUE pour interrompre la propagation, FALSE autrement. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean draw_black_background(GtkWidget *widget, cairo_t *cr, gpointer dummy) +bool gtk_app_about_dialog_create(GtkAppAboutDialog *dialog, GtkWindow *parent) { - int width; /* Largeur du composant */ - int height; /* Hauteur du composant */ + bool result; /* Bilan à retourner */ - width = gtk_widget_get_allocated_width(widget); - height = gtk_widget_get_allocated_height(widget); + result = true; - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + gtk_window_set_transient_for(GTK_WINDOW(dialog), parent); - cairo_rectangle(cr, 0, 0, width, height); - cairo_fill(cr); - - return FALSE; + return result; } diff --git a/src/gui/dialogs/about.css b/src/gui/dialogs/about.css new file mode 100644 index 0000000..af3fa97 --- /dev/null +++ b/src/gui/dialogs/about.css @@ -0,0 +1,9 @@ + +aboutdialog > box { + + background-color: black; + + background-image: url('resource:///re/chrysalide/framework/gui/dialogs/about/bg.png'); + background-repeat: no-repeat; + +} diff --git a/src/gui/dialogs/about.h b/src/gui/dialogs/about.h index f119f67..e569ad3 100644 --- a/src/gui/dialogs/about.h +++ b/src/gui/dialogs/about.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * about.h - prototypes pour la boîte de dialogue d'information sur le programme * - * Copyright (C) 2015-2020 Cyrille Bagard + * Copyright (C) 2015-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,9 +28,17 @@ #include <gtk/gtk.h> +#include "../../glibext/helpers.h" + + + +#define GTK_TYPE_APP_ABOUT_DIALOG (gtk_app_about_dialog_get_type()) + +DECLARE_GTYPE(GtkAppAboutDialog, gtk_app_about_dialog, GTK, APP_ABOUT_DIALOG); + /* Construit la fenêtre d'informations sur le logiciel. */ -GtkWidget *create_about_dialog(GtkWindow *, GtkBuilder **); +GtkWindow *gtk_app_about_dialog_new(GtkWindow *); diff --git a/src/gui/dialogs/about.ui b/src/gui/dialogs/about.ui index 0170508..7b519d2 100644 --- a/src/gui/dialogs/about.ui +++ b/src/gui/dialogs/about.ui @@ -1,151 +1,28 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.37.0 --> <interface> - <requires lib="gtk+" version="3.12"/> - <object class="GtkWindow" id="window"> - <property name="width-request">350</property> - <property name="height-request">430</property> - <property name="can-focus">False</property> - <property name="border-width">0</property> - <property name="title" translatable="yes">About</property> - <property name="resizable">False</property> - <property name="modal">True</property> - <property name="window-position">center-on-parent</property> - <property name="default-width">350</property> - <property name="default-height">430</property> - <property name="type-hint">dialog</property> - <signal name="key-press-event" handler="close_about_window_on_escape" swapped="no"/> - <child> - <object class="GtkFixed"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <signal name="draw" handler="draw_black_background" swapped="no"/> - <child> - <object class="GtkLabel"> - <property name="width-request">350</property> - <property name="height-request">20</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="margin-bottom">10</property> - <property name="label" translatable="yes"><span fgcolor='white'>Copyright (C) 2008-2020 Cyrille Bagard</span></property> - <property name="use-markup">True</property> - </object> - <packing> - <property name="y">400</property> - </packing> - </child> - <child> - <object class="GtkImage" id="logo"> - <property name="width-request">330</property> - <property name="height-request">300</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="resource">/org/chrysalide/gui/dialogs/about/chrysalide-full.png</property> - </object> - <packing> - <property name="x">10</property> - <property name="y">10</property> - </packing> - </child> - <child> - <object class="GtkImage" id="text"> - <property name="width-request">253</property> - <property name="height-request">42</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="resource">/org/chrysalide/gui/dialogs/about/chrysalide_text.png</property> - </object> - <packing> - <property name="x">48</property> - <property name="y">324</property> - </packing> - </child> - <child> - <object class="GtkImage" id="revision"> - <property name="width-request">14</property> - <property name="height-request">18</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="resource">/org/chrysalide/gui/dialogs/about/revision.png</property> - </object> - <packing> - <property name="x">149</property> - <property name="y">360</property> - </packing> - </child> - <child> - <object class="GtkImage" id="revision_0"> - <property name="width-request">14</property> - <property name="height-request">18</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - </object> - <packing> - <property name="x">163</property> - <property name="y">360</property> - </packing> - </child> - <child> - <object class="GtkImage" id="revision_1"> - <property name="width-request">14</property> - <property name="height-request">18</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - </object> - <packing> - <property name="x">177</property> - <property name="y">360</property> - </packing> - </child> - <child> - <object class="GtkImage" id="revision_2"> - <property name="width-request">14</property> - <property name="height-request">18</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - </object> - <packing> - <property name="x">191</property> - <property name="y">360</property> - </packing> - </child> - <child> - <object class="GtkImage" id="revision_3"> - <property name="width-request">14</property> - <property name="height-request">18</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - </object> - <packing> - <property name="x">205</property> - <property name="y">360</property> - </packing> - </child> - <child> - <object class="GtkImage" id="revision_4"> - <property name="width-request">14</property> - <property name="height-request">18</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - </object> - <packing> - <property name="x">219</property> - <property name="y">360</property> - </packing> - </child> - <child> - <object class="GtkImage" id="revision_5"> - <property name="width-request">14</property> - <property name="height-request">18</property> - <property name="visible">True</property> - <property name="can-focus">False</property> - </object> - <packing> - <property name="x">233</property> - <property name="y">360</property> - </packing> - </child> - </object> - </child> - </object> + + <template class="GtkAppAboutDialog" parent="GtkWindow"> + <property name="title" translatable="yes">About</property> + <property name="default-width">350</property> + <property name="default-height">430</property> + <property name="modal">true</property> + <property name="resizable">false</property> + + <child> + <object class="GtkBox"> + <property name="orientation">vertical</property> + + <child> + <object class="GtkLabel"> + <property name="margin-top">368</property> + <property name="margin-bottom">10</property> + <property name="label" translatable="yes"><span fgcolor='white'>Copyright (C) 2008-2025 Cyrille Bagard</span></property> + <property name="use-markup">True</property> + </object> + </child> + + </object> + </child> + + </template> </interface> diff --git a/src/gui/dialogs/gresource.xml b/src/gui/dialogs/gresource.xml index 0e12ef2..966d9c8 100644 --- a/src/gui/dialogs/gresource.xml +++ b/src/gui/dialogs/gresource.xml @@ -1,30 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <gresources> - <gresource prefix="/org/chrysalide/gui/dialogs"> + <gresource prefix="/re/chrysalide/framework/gui/dialogs"> + <file compressed="true">about.css</file> <file compressed="true">about.ui</file> - <file compressed="true">bookmark.ui</file> - <file compressed="true">export_graph.ui</file> - <file compressed="true">identity.ui</file> - <file compressed="true">loading.ui</file> <file compressed="true">preferences.ui</file> - <file compressed="true">prefs_fgraph.ui</file> - <file compressed="true">prefs_labels.ui</file> - <file compressed="true">snapshots.ui</file> - <file compressed="true">storage.ui</file> - </gresource> - <gresource prefix="/org/chrysalide/gui/dialogs/about"> - <file compressed="true" alias="chrysalide-full.png">../../../pixmaps/chrysalide-full.png</file> - <file compressed="true" alias="chrysalide_text.png">../../../pixmaps/chrysalide_text.png</file> - <file compressed="true" alias="revision.png">../../../pixmaps/revision.png</file> - <file compressed="true" alias="revision_0.png">../../../pixmaps/revision_0.png</file> - <file compressed="true" alias="revision_1.png">../../../pixmaps/revision_1.png</file> - <file compressed="true" alias="revision_2.png">../../../pixmaps/revision_2.png</file> - <file compressed="true" alias="revision_3.png">../../../pixmaps/revision_3.png</file> - <file compressed="true" alias="revision_4.png">../../../pixmaps/revision_4.png</file> - <file compressed="true" alias="revision_5.png">../../../pixmaps/revision_5.png</file> - <file compressed="true" alias="revision_6.png">../../../pixmaps/revision_6.png</file> - <file compressed="true" alias="revision_7.png">../../../pixmaps/revision_7.png</file> - <file compressed="true" alias="revision_8.png">../../../pixmaps/revision_8.png</file> - <file compressed="true" alias="revision_9.png">../../../pixmaps/revision_9.png</file> </gresource> </gresources> diff --git a/src/gui/dialogs/preferences-int.h b/src/gui/dialogs/preferences-int.h new file mode 100644 index 0000000..806f5f6 --- /dev/null +++ b/src/gui/dialogs/preferences-int.h @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * preferences-int.h - définitions internes pour la boîte de dialogue d'édition des préférences de l'utilisateur + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_DIALOGS_PREFERENCES_INT_H +#define _GUI_DIALOGS_PREFERENCES_INT_H + + +#include <stdbool.h> + + +#include "preferences.h" + + + +/* Fenêtre d'édition générale de la configuration (instance) */ +struct _GtkPreferencesDialog +{ + GtkWindow parent; /* A laisser en premier */ + + GtkLabel *side_title; /* Titre principal */ + GtkScrolledWindow *side_content; /* Liste des sections */ + GtkLabel *main_title; /* Titre de section */ + GtkScrolledWindow *main_content; /* Page de configuration */ + + GHashTable *navigations; /* Liste de sections en place */ + +}; + +/* Fenêtre d'édition générale de la configuration (classe) */ +struct _GtkPreferencesDialogClass +{ + GtkWindowClass parent; /* A laisser en premier */ + +}; + + +/* Met en place la boîte de dialogue pour les préférences. */ +bool gtk_preferences_dialog_create(GtkPreferencesDialog *, GtkWindow *); + + + +#endif /* _GUI_DIALOGS_PREFERENCES_INT_H */ diff --git a/src/gui/dialogs/preferences.c b/src/gui/dialogs/preferences.c index 4a3fb7c..0369241 100644 --- a/src/gui/dialogs/preferences.c +++ b/src/gui/dialogs/preferences.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * preferences.c - (re)définition de l'identité de l'utilisateur + * preferences.c - boîte de dialogue d'édition des préférences de l'utilisateur * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -24,129 +24,105 @@ #include "preferences.h" -#include <i18n.h> +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <gdk/gdkkeysyms.h> -#include "prefs_fgraph.h" -#include "prefs_labels.h" -#include "../../core/params.h" -#include "../../gtkext/easygtk.h" +#include <config.h> +#include "preferences-int.h" +#include "prefs/appearance.h" +#include "prefs/security.h" +#include "../../common/cpp.h" +#include "../../gtkext/tweak.h" +#include "../../plugins/pglist.h" +#include "../../plugins/tweakable.h" -/* Constructeur de panneau de paramétrage */ -typedef GtkWidget * (* prefs_panel_creation_cb) (GtkBuilder **); -/* Chargement de la configuration */ -typedef void (* prefs_config_update_cb) (GtkBuilder *, GGenConfig *); -/* Description d'un noeud de préférences */ -typedef struct _pref_node_desc_t -{ - prefs_panel_creation_cb create; /* Procédure de création */ - prefs_config_update_cb load; /* Procédure de chargement */ - prefs_config_update_cb store; /* Procédure d'enregistrement */ - - const char *name; /* Désignation interne */ - const char *title; /* Désignation humaine */ - - GtkBuilder *builder; /* Constructeur GTK */ - GtkWidget *panel; /* Panneau GTK */ - - struct _pref_node_desc_t *children; /* Sous-arborescence */ - -} pref_node_desc_t; - - -#define PREF_NODE_NULL_ENTRY { .title = NULL } +/* --------------------------- BASES DE BOITE DE DIALOGUE --------------------------- */ -/* Liste des paramétrages à afficher */ -static pref_node_desc_t _prefs_nodes[] = { +/* Procède à l'initialisation de la fenêtre des paramètres. */ +static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *); - { - .create = NULL, - - .title = "Analysis", +/* Procède à l'initialisation de la fenêtre des paramètres. */ +static void gtk_preferences_dialog_init(GtkPreferencesDialog *); - .children = (pref_node_desc_t []){ +/* Supprime toutes les références externes. */ +static void gtk_preferences_dialog_dispose(GObject *); - { - .create = create_labels_preferences, - .load = load_labels_configuration, - .store = store_labels_configuration, +/* Procède à la libération totale de la mémoire. */ +static void gtk_preferences_dialog_finalize(GObject *); - .name = "labels", - .title = "Colored labels", +/* Fournit la liste de section désignée par un nom. */ +static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *, const char *, bool); - }, +/* Réagit à un changement de sélection dans les sections. */ +static void gtk_preferences_dialog_on_row_selected(GtkListBox *, GtkListBoxRow *, GtkPreferencesDialog *); - PREF_NODE_NULL_ENTRY - } - }, +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ - { - .create = NULL, - .title = "Editor", - .children = (pref_node_desc_t []){ - { - .create = create_fgraph_preferences, - .load = load_fgraph_configuration, - .store = store_fgraph_configuration, - .name = "fgraph", - .title = "Function graph", +/* ---------------------------------------------------------------------------------- */ +/* BASES DE BOITE DE DIALOGUE */ +/* ---------------------------------------------------------------------------------- */ - }, - - PREF_NODE_NULL_ENTRY - - } - }, +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkPreferencesDialog, gtk_preferences_dialog, GTK_TYPE_WINDOW); - PREF_NODE_NULL_ENTRY - -}; +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de la fenêtre des paramètres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ -/* Eléments de la liste de sections */ -typedef enum _PrefListItem +static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *class) { - PLI_TITLE, /* Etiquette de la section */ - PLI_PANEL, /* Panneau graphique associé */ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ -} PrefListItem; + object = G_OBJECT_CLASS(class); + object->dispose = gtk_preferences_dialog_dispose; + object->finalize = gtk_preferences_dialog_finalize; -/* Ajoute un panneau de paramétrage à la boîte de dialogue. */ -static void add_preferences_node(GtkTreeStore *, GtkTreeIter *, GGenConfig *, GtkStack *, pref_node_desc_t *); + widget = GTK_WIDGET_CLASS(class); -/* Affiche le panneau correspondant au noeud sélectionné. */ -static void on_prefs_selection_changed(GtkTreeSelection *, GtkBuilder *); + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/preferences.ui"); -/* Lance la sauvegarde d'éléments de paramétrage. */ -static void store_preferences_node(GGenConfig *, pref_node_desc_t *); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, side_title); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, side_content); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, main_title); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, main_content); -/* Sauvegarde l'ensemble des paramètres de configuration. */ -static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *); + /* Active une action native (cf. https://docs.gtk.org/gtk4/class.Window.html#actions) */ + gtk_widget_class_add_binding_action(widget, GDK_KEY_Escape, 0 /* GDK 4.14 : GDK_NO_MODIFIER_MASK */, "window.close", NULL); +} /****************************************************************************** * * -* Paramètres : store = arborescence des sections à compléter. * -* parent = point d'insertion du parent. * -* config = configuration globale à charger. * -* stack = pile de composants GTK à constituer. * -* node = noeud de description courant à traiter. * +* Paramètres : dialog = composant GTK à initialiser. * * * -* Description : Ajoute un panneau de paramétrage à la boîte de dialogue. * +* Description : Procède à l'initialisation de la fenêtre des paramètres. * * * * Retour : - * * * @@ -154,95 +130,152 @@ static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *); * * ******************************************************************************/ -static void add_preferences_node(GtkTreeStore *store, GtkTreeIter *parent, GGenConfig *config, GtkStack *stack, pref_node_desc_t *node) +static void gtk_preferences_dialog_init(GtkPreferencesDialog *dialog) { - GtkTreeIter iter; /* Point d'insertion */ - pref_node_desc_t *child; /* Sous-élément à traiter */ + size_t i; /* Boucle de parcours */ + const tweak_info_t *info; /* Informations à considérer */ + GtkListBox *navigation; /* Liste de sections à afficher*/ + GtkTweakSection *section; /* Nouvelle section à présenter*/ + tweak_info_t *dyn_infos; /* Informations supplémentaires*/ + size_t dyn_count; /* Quantité de ces informations*/ + + tweak_info_t infos[] = { + TWEAK_SIMPLE_DEF("root", "Basics", + "security-high-symbolic", "appearance", "Appearance", GTK_TYPE_APPEARANCE_TWEAK_PANEL), + TWEAK_SIMPLE_DEF("root", "Basics", + "security-high-symbolic", "security", "Security", GTK_TYPE_SECURITY_TWEAK_PANEL), + }; + + gtk_widget_init_template(GTK_WIDGET(dialog)); - if (node->create == NULL) + dialog->navigations = g_hash_table_new_full(g_str_hash, g_str_equal, free, g_object_unref); + + /* Chargement des sections fixes */ + + for (i = 0; i < ARRAY_SIZE(infos); i++) { - node->builder = NULL; - node->panel = NULL; + info = &infos[i]; + + navigation = gtk_preferences_dialog_get_navigation(dialog, info->parent, true); + assert(navigation != NULL); + + section = gtk_tweak_section_new(info); + + gtk_list_box_append(navigation, GTK_WIDGET(section)); + } - else + + /* Chargement des sections dynamiques */ + + dyn_infos = get_tweakable_plugins_info(&dyn_count); + + for (i = 0; i < dyn_count; i++) { - node->panel = node->create(&node->builder); + info = &dyn_infos[i]; - node->load(node->builder, config); + navigation = gtk_preferences_dialog_get_navigation(dialog, info->parent, true); + assert(navigation != NULL); - gtk_widget_show(node->panel); + section = gtk_tweak_section_new(info); - gtk_stack_add_named(stack, node->panel, node->name); + gtk_list_box_append(navigation, GTK_WIDGET(section)); } - gtk_tree_store_append(store, &iter, parent); + if (dyn_infos != NULL) + free(dyn_infos); + + /* Affichage de la liste racine */ - gtk_tree_store_set(store, &iter, - PLI_TITLE, _(node->title), - PLI_PANEL, node->panel, - -1); + navigation = gtk_preferences_dialog_get_navigation(dialog, "root", false); + assert(navigation != NULL); - if (node->children != NULL) - for (child = node->children; child->title != NULL; child++) - add_preferences_node(store, &iter, config, stack, child); + g_signal_connect(navigation, "row-selected", + G_CALLBACK(gtk_preferences_dialog_on_row_selected), dialog); + + gtk_scrolled_window_set_child(dialog->side_content, GTK_WIDGET(navigation)); + + unref_object(navigation); } /****************************************************************************** * * -* Paramètres : parent = fenêtre principale de l'éditeur. * -* outb = constructeur à détruire après usage. [OUT] * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Propose une boîte de dialogue pour la configuration générale.* +* Description : Supprime toutes les références externes. * * * -* Retour : Adresse de la fenêtre mise en place. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb) +static void gtk_preferences_dialog_dispose(GObject *object) { - GtkWidget *result; /* Fenêtre à renvoyer */ - GtkBuilder *builder; /* Constructeur utilisé */ - GGenConfig *config; /* Configuration globale */ - GtkStack *stack; /* Pile à mettre à jour */ - GtkTreeStore *store; /* Arborescence des sections */ - pref_node_desc_t *iter; /* Boucle de parcours */ - GtkTreeView *treeview; /* Arborescence principale */ + GtkPreferencesDialog *dialog; /* Version spécialisée */ + + dialog = GTK_PREFERENCES_DIALOG(object); - builder = gtk_builder_new_from_resource("/org/chrysalide/gui/dialogs/preferences.ui"); - *outb = builder; + if (dialog->navigations != NULL) + { + /** + * Cf. documentation de g_hash_table_new_full(). + */ + g_hash_table_remove_all(dialog->navigations); - result = GTK_WIDGET(gtk_builder_get_object(builder, "window")); + g_hash_table_unref(dialog->navigations); + dialog->navigations = NULL; - gtk_window_set_transient_for(GTK_WINDOW(result), parent); + } - /* Intégration des différentes sections */ + gtk_widget_dispose_template(GTK_WIDGET(dialog), GTK_TYPE_PREFERENCES_DIALOG); + + G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->dispose(object); + +} - config = get_main_configuration(); - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - store = GTK_TREE_STORE(gtk_builder_get_object(builder, "pref_list")); +static void gtk_preferences_dialog_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->finalize(object); - for (iter = _prefs_nodes; iter->title != NULL; iter++) - add_preferences_node(store, NULL, config, stack, iter); +} - treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); - gtk_tree_view_expand_all(treeview); +/****************************************************************************** +* * +* Paramètres : parent = fenêtre parente à surpasser. * +* * +* Description : Construit une boîte de dialogue pour les préférences. * +* * +* Retour : Adresse de la fenêtre mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ - /* Connexion des signaux */ +GtkWindow *gtk_preferences_dialog_new(GtkWindow *parent) +{ + GtkWindow *result; /* Boite de dialogue à renvoyer*/ - gtk_builder_add_callback_symbols(builder, - BUILDER_CALLBACK(on_prefs_selection_changed), - BUILDER_CALLBACK(on_prefs_apply_button_clicked), - NULL); + result = g_object_new(GTK_TYPE_PREFERENCES_DIALOG, NULL); - gtk_builder_connect_signals(builder, builder); + if (!gtk_preferences_dialog_create(GTK_PREFERENCES_DIALOG(result), parent)) + g_clear_object(&result); return result; @@ -251,79 +284,85 @@ GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb) /****************************************************************************** * * -* Paramètres : selection = sélection courante de l'arborescence des options.* -* builder = constructeur GTK avec toutes les références. * +* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * +* parent = fenêtre parente à surpasser. * * * -* Description : Affiche le panneau correspondant au noeud sélectionné. * +* Description : Met en place la boîte de dialogue pour les préférences. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void on_prefs_selection_changed(GtkTreeSelection *selection, GtkBuilder *builder) +bool gtk_preferences_dialog_create(GtkPreferencesDialog *dialog, GtkWindow *parent) { - GtkTreeModel *model; /* Gestionnaire de données */ - GtkTreeIter iter; /* Position courante */ - GtkWidget *panel; /* Panneau à mettre en avant */ - GtkStack *stack; /* Pile à mettre à jour */ - - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - { - gtk_tree_model_get(model, &iter, PLI_PANEL, &panel, -1); + bool result; /* Bilan à retourner */ - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); + result = true; - if (panel == NULL) - gtk_stack_set_visible_child_name(stack, "empty"); - - else - { - gtk_stack_set_visible_child(stack, panel); + gtk_window_set_transient_for(GTK_WINDOW(dialog), parent); - g_object_unref(G_OBJECT(panel)); - - } - - } + return result; } /****************************************************************************** * * -* Paramètres : config = configuration globale à actualiser. * -* node = noeud de description courant à traiter. * +* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * +* key = désignation de la liste à fournir. * +* create = autorisation d'une création si besoin est. * * * -* Description : Lance la sauvegarde d'éléments de paramétrage. * +* Description : Fournit la liste de section désignée par un nom. * * * -* Retour : - * +* Retour : Composant graphique à utiliser. * * * * Remarques : - * * * ******************************************************************************/ -static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node) +static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *dialog, const char *key, bool create) { - pref_node_desc_t *child; /* Sous-élément à traiter */ + GtkListBox *result; /* Instance à retourner */ +#ifndef NDEBUG + gboolean status; /* Bilan d'une insertion */ +#endif - if (node->create != NULL) - node->store(node->builder, config); + result = g_hash_table_lookup(dialog->navigations, key); - if (node->children != NULL) - for (child = node->children; child->title != NULL; child++) - store_preferences_node(config, child); + if (result == NULL && create) + { + result = GTK_LIST_BOX(gtk_list_box_new()); + g_object_ref_sink(G_OBJECT(result)); + + gtk_list_box_set_selection_mode(result, GTK_SELECTION_BROWSE); + gtk_widget_add_css_class(GTK_WIDGET(result), "navigation-sidebar"); + +#ifndef NDEBUG + status = g_hash_table_insert(dialog->navigations, strdup(key), result); + assert(status); +#else + g_hash_table_insert(dialog->navigations, key, result); +#endif + + } + + if (result != NULL) + ref_object(result); + + return result; } /****************************************************************************** * * -* Paramètres : button = bouton GTK à l'origine de l'opération. * -* builder = constructeur GTK avec toutes les références. * +* Paramètres : navigation = liste concernée par l'événement. * +* selected = élément sélectionné (voire NULL). * +* dialog = boîte de dialogue à initialiser pleinement. * * * -* Description : Sauvegarde l'ensemble des paramètres de configuration. * +* Description : Réagit à un changement de sélection dans les sections. * * * * Retour : - * * * @@ -331,14 +370,41 @@ static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node) * * ******************************************************************************/ -static void on_prefs_apply_button_clicked(GtkButton *button, GtkBuilder *builder) +static void gtk_preferences_dialog_on_row_selected(GtkListBox *navigation, GtkListBoxRow *selected, GtkPreferencesDialog *dialog) { - GGenConfig *config; /* Configuration globale */ - pref_node_desc_t *iter; /* Boucle de parcours */ + GtkTweakSection *section; /* Nature réelle sélectionnée */ + GType type; /* Type de panneau de config. */ + GtkWidget *panel; /* Nouveau panneau à présenter */ + + if (selected != NULL) + { + if (!gtk_tweak_section_has_sub_section(GTK_TWEAK_SECTION(selected))) + { + section = GTK_TWEAK_SECTION(selected); + + gtk_label_set_text(dialog->main_title, gtk_tweak_section_get_label(section)); + + type = gtk_tweak_section_get_panel(section); + panel = g_object_new(type, NULL); - config = get_main_configuration(); + gtk_scrolled_window_set_child(dialog->main_content, panel); - for (iter = _prefs_nodes; iter->title != NULL; iter++) - store_preferences_node(config, iter); + } + + else + { + + // TODO + + } + + } } + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + diff --git a/src/gui/dialogs/preferences.h b/src/gui/dialogs/preferences.h index d04af72..e291ea8 100644 --- a/src/gui/dialogs/preferences.h +++ b/src/gui/dialogs/preferences.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * preferences.h - prototypes pour la (re)définition de l'identité de l'utilisateur + * preferences.h - prototypes pour la boîte de dialogue d'édition des préférences de l'utilisateur * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,9 +28,17 @@ #include <gtk/gtk.h> +#include "../../glibext/helpers.h" -/* Propose une boîte de dialogue pour la configuration générale. */ -GtkWidget *create_preferences_dialog(GtkWindow *, GtkBuilder **); + + +#define GTK_TYPE_PREFERENCES_DIALOG (gtk_preferences_dialog_get_type()) + +DECLARE_GTYPE(GtkPreferencesDialog, gtk_preferences_dialog, GTK, PREFERENCES_DIALOG); + + +/* Construit une boîte de dialogue pour les préférences. */ +GtkWindow *gtk_preferences_dialog_new(GtkWindow *); diff --git a/src/gui/dialogs/preferences.ui b/src/gui/dialogs/preferences.ui index 251ef46..950242a 100644 --- a/src/gui/dialogs/preferences.ui +++ b/src/gui/dialogs/preferences.ui @@ -1,156 +1,111 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.21.0 --> <interface> - <requires lib="gtk+" version="3.20"/> - <object class="GtkTreeStore" id="pref_list"> - <columns> - <!-- column-name title --> - <column type="gchararray"/> - <!-- column-name panel --> - <column type="GObject"/> - </columns> - </object> - <object class="GtkDialog" id="window"> - <property name="can_focus">False</property> - <property name="title" translatable="yes">General preferences</property> - <property name="modal">True</property> - <property name="default_width">800</property> - <property name="default_height">500</property> - <property name="type_hint">dialog</property> - <child internal-child="vbox"> - <object class="GtkBox"> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="spacing">2</property> - <child internal-child="action_area"> - <object class="GtkButtonBox"> - <property name="can_focus">False</property> - <property name="margin_left">8</property> - <property name="margin_right">8</property> - <property name="margin_top">8</property> - <property name="margin_bottom">8</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="button1"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="button2"> - <property name="label">gtk-apply</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - <signal name="clicked" handler="on_prefs_apply_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> + + <template class="GtkPreferencesDialog" parent="GtkWindow"> + + <property name="default-width">980</property> + <property name="default-height">640</property> + <property name="modal">true</property> + <property name="resizable">true</property> + + <child type="titlebar"> + <object class="GtkBox"> + <child> + <object class="GtkHeaderBar" id="sidebar-header"> + <property name="decoration-layout">:</property> + + <property name="title-widget"> + <object class="GtkLabel" id="side_title"> + <property name="label" translatable="yes">Parameters</property> + <property name="single-line-mode">True</property> + <property name="ellipsize">end</property> + <property name="width-chars">5</property> + <style> + <class name="title"/> + </style> + </object> + </property> + + <child type="start"> + <object class="GtkToggleButton"> + <property name="icon-name">edit-find-symbolic</property> + <property name="valign">center</property> + </object> + </child> + </object> + </child> + + <!-- Séparation centrale --> + <child> + <object class="GtkSeparator"> + <property name="orientation">vertical</property> + <property name="css-name">sidebar</property> + </object> + </child> + + <child> + <object class="GtkHeaderBar" id="main-header"> + <property name="hexpand">1</property> + + <property name="title-widget"> + <object class="GtkLabel" id="main_title"> + <property name="label" translatable="yes"></property> + <property name="single-line-mode">True</property> + <property name="ellipsize">end</property> + <property name="width-chars">5</property> + <style> + <class name="title"/> + </style> + </object> + </property> + + </object> + </child> + </object> </child> + <child> - <object class="GtkPaned"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="position">200</property> - <property name="wide_handle">True</property> - <child> - <object class="GtkScrolledWindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="margin_left">8</property> - <property name="margin_right">8</property> - <property name="margin_top">8</property> - <property name="shadow_type">in</property> + <object class="GtkBox"> <child> - <object class="GtkTreeView" id="treeview"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="model">pref_list</property> - <property name="headers_visible">False</property> - <child internal-child="selection"> - <object class="GtkTreeSelection"> - <signal name="changed" handler="on_prefs_selection_changed" swapped="no"/> - </object> - </child> - <child> - <object class="GtkTreeViewColumn"> - <property name="title" translatable="yes">column</property> - <child> - <object class="GtkCellRendererText"/> - <attributes> - <attribute name="text">0</attribute> - </attributes> - </child> - </object> - </child> - </object> + <object class="GtkScrolledWindow" id="side_content"> + <property name="hexpand">false</property> + <property name="vexpand">true</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">automatic</property> + </object> + </child> + + <!-- Séparation centrale --> + <child> + <object class="GtkSeparator"> + <property name="orientation">vertical</property> + <property name="css-name">sidebar</property> + </object> </child> - </object> - <packing> - <property name="resize">False</property> - <property name="shrink">True</property> - </packing> - </child> - <child> - <object class="GtkStack" id="stack"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="margin_left">8</property> - <property name="margin_right">8</property> - <property name="margin_top">8</property> + <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="name">empty</property> - </packing> + <object class="GtkScrolledWindow" id="main_content"> + <property name="hexpand">1</property> + <property name="vexpand">1</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">automatic</property> + </object> </child> - </object> - <packing> - <property name="resize">True</property> - <property name="shrink">True</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> + </object> </child> - </object> - </child> - <action-widgets> - <action-widget response="-6">button1</action-widget> - <action-widget response="-10">button2</action-widget> - </action-widgets> - <child> - <placeholder/> - </child> - </object> + + <object class="GtkSizeGroup"> + <widgets> + <widget name="sidebar-header"/> + <widget name="side_content"/> + </widgets> + </object> + <object class="GtkSizeGroup"> + <widgets> + <widget name="main-header"/> + <widget name="main_content"/> + </widgets> + </object> + + </template> </interface> diff --git a/src/gui/dialogs/prefs/Makefile.am b/src/gui/dialogs/prefs/Makefile.am new file mode 100644 index 0000000..50bc3c8 --- /dev/null +++ b/src/gui/dialogs/prefs/Makefile.am @@ -0,0 +1,34 @@ + +BUILT_SOURCES = resources.h resources.c + +noinst_LTLIBRARIES = libguidialogsprefs.la + +UI_FILES = \ + appearance.ui \ + security.ui + +libguidialogsprefs_la_SOURCES = \ + appearance-int.h \ + appearance.h appearance.c \ + resources.h resources.c \ + security-int.h \ + security.h security.c + +libguidialogsprefs_la_CFLAGS = $(LIBGTK4_CFLAGS) + + +devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) + +dev_HEADERS = $(libguidialogsprefs_la_SOURCES:%c=) + + +resources.c: gresource.xml $(UI_FILES) + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_dialogs_prefs gresource.xml + +resources.h: gresource.xml + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name gui_dialogs_prefs gresource.xml + + +CLEANFILES = resources.h resources.c + +EXTRA_DIST = gresource.xml $(UI_FILES) diff --git a/src/gui/dialogs/prefs/appearance-int.h b/src/gui/dialogs/prefs/appearance-int.h new file mode 100644 index 0000000..886a562 --- /dev/null +++ b/src/gui/dialogs/prefs/appearance-int.h @@ -0,0 +1,64 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * appearance-int.h - définitions internes pour la configuration des paramètres liés aux apparences + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_DIALOGS_PREFS_APPEARANCE_INT_H +#define _GUI_DIALOGS_PREFS_APPEARANCE_INT_H + + +#include "appearance.h" + + + +/* Composant d'édition des paramètres liés aux apparences (instance) */ +struct _GtkAppearanceTweakPanel +{ + GtkBox parent; /* A laisser en premier */ + + /* Disposition des panneaux */ + + GSettings *tiles_settings; /* Configuration sollicitée */ + + GtkWidget *layout_preview; /* Zone d'aperçu de dispositon*/ + + GtkWidget *top_panel; /* Composant d'aperçu #1 */ + GtkWidget *left_panel; /* Composant d'aperçu #2 */ + GtkWidget *right_panel; /* Composant d'aperçu #3 */ + GtkWidget *bottom_panel; /* Composant d'aperçu #4 */ + + GtkCheckButton *left_top_reach; /* Paramètre de disposition #1 */ + GtkCheckButton *left_bottom_reach; /* Paramètre de disposition #2 */ + GtkCheckButton *right_top_reach; /* Paramètre de disposition #3 */ + GtkCheckButton *right_bottom_reach; /* Paramètre de disposition #4 */ + +}; + +/* Composant d'édition des paramètres liés aux apparences (classe) */ +struct _GtkAppearanceTweakPanelClass +{ + GtkBoxClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GUI_DIALOGS_PREFS_APPEARANCE_INT_H */ diff --git a/src/gui/dialogs/prefs/appearance.c b/src/gui/dialogs/prefs/appearance.c new file mode 100644 index 0000000..a8585fa --- /dev/null +++ b/src/gui/dialogs/prefs/appearance.c @@ -0,0 +1,251 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * appearance.c - configuration des paramètres liés aux apparences + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "appearance.h" + + +#include "appearance-int.h" +#include "../../../gtkext/grid.h" +#include "../../../gtkext/helpers.h" + + + +/* Procède à l'initialisation de classe des configurations. */ +static void gtk_appearance_tweak_panel_class_init(GtkAppearanceTweakPanelClass *); + +/* Procède à l'initialisation des configurations de sécurité. */ +static void gtk_appearance_tweak_panel_init(GtkAppearanceTweakPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_appearance_tweak_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_appearance_tweak_panel_finalize(GObject *); + +/* Réagit à un changement de paramètre de configuration. */ +static void gtk_appearance_tweak_panel_on_tiles_settings_changed(GSettings *, const gchar *, GtkAppearanceTweakPanel *); + +/* Change la disposition des panneaux de la fenêtre principale. */ +static void gtk_appearance_tweak_panel_on_panel_reach_toggled(GtkCheckButton *, GtkAppearanceTweakPanel *); + + +/* Indique le type du composant de configuration des notes. */ +G_DEFINE_TYPE(GtkAppearanceTweakPanel, gtk_appearance_tweak_panel, GTK_TYPE_BOX); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de classe des configurations. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_appearance_tweak_panel_class_init(GtkAppearanceTweakPanelClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_appearance_tweak_panel_dispose; + object->finalize = gtk_appearance_tweak_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/prefs/appearance.ui"); + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_appearance_tweak_panel_on_panel_reach_toggled)); + + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, layout_preview); + + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, top_panel); + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, left_panel); + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, right_panel); + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, bottom_panel); + + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, left_top_reach); + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, left_bottom_reach); + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, right_top_reach); + gtk_widget_class_bind_template_child(widget, GtkAppearanceTweakPanel, right_bottom_reach); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation des configurations de sécurité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_appearance_tweak_panel_init(GtkAppearanceTweakPanel *panel) +{ + LayoutReachOptions options; /* Détails de disposition */ + + gtk_widget_init_template(GTK_WIDGET(panel)); + + panel->tiles_settings = g_settings_new_with_path("re.chrysalide.framework.tiledgrid", + "/re/chrysalide/framework/gui/tiles/"); + + options = g_settings_get_flags(panel->tiles_settings, "layout"); + + gtk_check_button_set_active(panel->left_top_reach, options & LRO_LEFT_TOP_REACH); + gtk_check_button_set_active(panel->left_bottom_reach, options & LRO_LEFT_BOTTOM_REACH); + gtk_check_button_set_active(panel->right_top_reach, options & LRO_RIGHT_TOP_REACH); + gtk_check_button_set_active(panel->right_bottom_reach, options & LRO_RIGHT_BOTTOM_REACH); + + gtk_appearance_tweak_panel_on_tiles_settings_changed(panel->tiles_settings, "layout", panel); + + g_signal_connect(panel->tiles_settings, "changed", + G_CALLBACK(gtk_appearance_tweak_panel_on_tiles_settings_changed), panel); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_appearance_tweak_panel_dispose(GObject *object) +{ + GtkAppearanceTweakPanel *panel; /* Version spécialisée */ + + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_APPEARANCE_TWEAK_PANEL); + + panel = GTK_APPEARANCE_TWEAK_PANEL(object); + + g_clear_object(&panel->tiles_settings); + + G_OBJECT_CLASS(gtk_appearance_tweak_panel_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_appearance_tweak_panel_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_appearance_tweak_panel_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : settings = ensemble de paramètres connaissant une évolution. * +* key = identifiant du paramètre ayant changé. * +* panel = panneau de paramétrage concerné par l'appel. * +* * +* Description : Réagit à un changement de paramètre de configuration. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_appearance_tweak_panel_on_tiles_settings_changed(GSettings *settings, const gchar *key, GtkAppearanceTweakPanel *panel) +{ + LayoutReachOptions options; /* Détails de disposition */ + + if (strcmp(key, "layout") == 0) + { + options = g_settings_get_flags(panel->tiles_settings, "layout"); + + apply_tiling_grid_layout(GTK_GRID(panel->layout_preview), options, (GtkWidget *[]) { + panel->top_panel, panel->left_panel, panel->right_panel, panel->bottom_panel + }); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : button = composant radio dont l'état vient de basculer. * +* panel = panneau d'édition des préférences courant. * +* * +* Description : Change la disposition des panneaux de la fenêtre principale. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_appearance_tweak_panel_on_panel_reach_toggled(GtkCheckButton *button, GtkAppearanceTweakPanel *panel) +{ + gboolean left_top_reach; /* Paramètre de disposition #1 */ + gboolean left_bottom_reach; /* Paramètre de disposition #2 */ + gboolean right_top_reach; /* Paramètre de disposition #3 */ + gboolean right_bottom_reach; /* Paramètre de disposition #4 */ + LayoutReachOptions options; /* Options à appliquer */ + + /* Récupération des indications */ + + left_top_reach = gtk_check_button_get_active(panel->left_top_reach); + left_bottom_reach = gtk_check_button_get_active(panel->left_bottom_reach); + right_top_reach = gtk_check_button_get_active(panel->right_top_reach); + right_bottom_reach = gtk_check_button_get_active(panel->right_bottom_reach); + + /* Conversion et application */ + + options = LRO_NONE; + + if (left_top_reach) options |= LRO_LEFT_TOP_REACH; + if (left_bottom_reach) options |= LRO_LEFT_BOTTOM_REACH; + if (right_top_reach) options |= LRO_RIGHT_TOP_REACH; + if (right_bottom_reach) options |= LRO_RIGHT_BOTTOM_REACH; + + g_settings_set_flags(panel->tiles_settings, "layout", options); + +} diff --git a/src/gui/dialogs/prefs/appearance.h b/src/gui/dialogs/prefs/appearance.h new file mode 100644 index 0000000..916b194 --- /dev/null +++ b/src/gui/dialogs/prefs/appearance.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * appearance.h - prototypes pour la configuration des paramètres liés aux apparences + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_DIALOGS_PREFS_APPEARANCE_H +#define _GUI_DIALOGS_PREFS_APPEARANCE_H + + +#include <gtk/gtk.h> + + +#include "../../../glibext/helpers.h" + + + +#define GTK_TYPE_APPEARANCE_TWEAK_PANEL (gtk_appearance_tweak_panel_get_type()) + +DECLARE_GTYPE(GtkAppearanceTweakPanel, gtk_appearance_tweak_panel, GTK, APPEARANCE_TWEAK_PANEL); + + + +#endif /* _GUI_DIALOGS_PREFS_APPEARANCE_H */ diff --git a/src/gui/dialogs/prefs/appearance.ui b/src/gui/dialogs/prefs/appearance.ui new file mode 100644 index 0000000..3410115 --- /dev/null +++ b/src/gui/dialogs/prefs/appearance.ui @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkAppearanceTweakPanel" parent="GtkBox"> + + <property name="orientation">vertical</property> + <property name="margin-start">20</property> + <property name="margin-end">20</property> + <property name="margin-top">20</property> + <property name="margin-bottom">20</property> + <property name="spacing">10</property> + + <!-- Disposition des panneaux --> + + <child> + <object class="GtkLabel"> + <property name="label">Layout</property> + <property name="use-markup">true</property> + <property name="xalign">0</property> + <style> + <class name="heading"/> + </style> + </object> + </child> + + <child> + <object class="GtkLabel"> + <property name="label">Layout defines the global panel organization of the main window.</property> + <property name="wrap">true</property> + <property name="use-markup">true</property> + <property name="xalign">0</property> + <style> + <class name="dim-label"/> + </style> + </object> + </child> + + + <child> + <object class="GtkBox"> + <property name="orientation">horizontal</property> + + <!-- Aperçu --> + + <child> + <object class="GtkGrid" id="layout_preview"> + <property name="margin-start">20</property> + <property name="margin-end">20</property> + <property name="margin-top">10</property> + <property name="margin-bottom">10</property> + <property name="row-spacing">5</property> + <property name="column-spacing">5</property> + <property name="hexpand">false</property> + <property name="vexpand">false</property> + <property name="height-request">200</property> + <property name="width-request">300</property> + + <child> + <object class="GtkFrame" id="top_panel"> + <property name="hexpand">true</property> + <property name="vexpand">false</property> + <property name="height-request">20</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="column-span">3</property> + </layout> + <style> + <class name="view"/> + </style> + </object> + </child> + + <child> + <object class="GtkFrame" id="left_panel"> + <property name="hexpand">false</property> + <property name="vexpand">true</property> + <property name="width-request">50</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + <style> + <class name="view"/> + </style> + </object> + </child> + + <child> + <object class="GtkFrame"> + <property name="hexpand">true</property> + <property name="vexpand">true</property> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + <style> + <class name="view"/> + </style> + </object> + </child> + + <child> + <object class="GtkFrame" id="right_panel"> + <property name="hexpand">false</property> + <property name="vexpand">true</property> + <property name="width-request">50</property> + <layout> + <property name="column">2</property> + <property name="row">1</property> + </layout> + <style> + <class name="view"/> + </style> + </object> + </child> + + <child> + <object class="GtkFrame" id="bottom_panel"> + <property name="hexpand">true</property> + <property name="vexpand">false</property> + <property name="height-request">50</property> + <layout> + <property name="column">0</property> + <property name="row">2</property> + <property name="column-span">3</property> + </layout> + <style> + <class name="view"/> + </style> + </object> + </child> + + </object> + </child> + + <!-- Options --> + + <child> + <object class="GtkBox"> + <property name="orientation">vertical</property> + <property name="valign">center</property> + <property name="spacing">10</property> + + <child> + <object class="GtkCheckButton" id="left_top_reach"> + <property name="label">Left panels reach the top of the window</property> + <property name="active">false</property> + <signal name="toggled" handler="gtk_appearance_tweak_panel_on_panel_reach_toggled"/> + </object> + </child> + + <child> + <object class="GtkCheckButton" id="left_bottom_reach"> + <property name="label">Left panels reach the bottom of the window</property> + <property name="active">false</property> + <signal name="toggled" handler="gtk_appearance_tweak_panel_on_panel_reach_toggled"/> + </object> + </child> + + <child> + <object class="GtkCheckButton" id="right_top_reach"> + <property name="label">Right panels reach the top of the window</property> + <property name="active">false</property> + <signal name="toggled" handler="gtk_appearance_tweak_panel_on_panel_reach_toggled"/> + </object> + </child> + + <child> + <object class="GtkCheckButton" id="right_bottom_reach"> + <property name="label">Right panels reach the bottom of the window</property> + <property name="active">false</property> + <signal name="toggled" handler="gtk_appearance_tweak_panel_on_panel_reach_toggled"/> + </object> + </child> + + </object> + </child> + + </object> + </child> + + </template> +</interface> diff --git a/src/gui/dialogs/prefs/gresource.xml b/src/gui/dialogs/prefs/gresource.xml new file mode 100644 index 0000000..ad0f97f --- /dev/null +++ b/src/gui/dialogs/prefs/gresource.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/re/chrysalide/framework/gui/dialogs/prefs"> + <file compressed="true">appearance.ui</file> + <file compressed="true">security.ui</file> + </gresource> +</gresources> diff --git a/src/gui/dialogs/prefs/security-int.h b/src/gui/dialogs/prefs/security-int.h new file mode 100644 index 0000000..be7867c --- /dev/null +++ b/src/gui/dialogs/prefs/security-int.h @@ -0,0 +1,64 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * security-int.h - définitions internes pour la configuration des paramètres liés à la sécurité + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_DIALOGS_PREFS_SECURITY_INT_H +#define _GUI_DIALOGS_PREFS_SECURITY_INT_H + + +#include "security.h" +#include "../../../glibext/secstorage.h" + + + +/* Composant d'édition des paramètres de sécurité (instance) */ +struct _GtkSecurityTweakPanel +{ + GtkBox parent; /* A laisser en premier */ + + /* Stockage sécurisé */ + + GSecretStorage *storage; /* Stockage des secrets */ + + GtkWidget *current_primary_passwd; /* Mot de passe courant */ + + GtkWidget *new_primary_passwd; /* Nouveau mot de passe */ + GtkWidget *new_primary_passwd_lbl; /* Etiquette associée */ + GtkWidget *new_primary_passwd_2; /* Nouveau mot de passe (#2) */ + GtkWidget *new_primary_passwd_2_lbl; /* Etiquette associée (#2) */ + + GtkWidget *set_password; /* Bouton de définition de mdp */ + GtkWidget *change_password; /* Bouton de changement de mdp */ + GtkWidget *remove_password; /* Bouton de suppression de mdp*/ + +}; + +/* Composant d'édition des paramètres de sécurité (classe) */ +struct _GtkSecurityTweakPanelClass +{ + GtkBoxClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GUI_DIALOGS_PREFS_SECURITY_INT_H */ diff --git a/src/gui/dialogs/prefs/security.c b/src/gui/dialogs/prefs/security.c new file mode 100644 index 0000000..2b82339 --- /dev/null +++ b/src/gui/dialogs/prefs/security.c @@ -0,0 +1,400 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * security.c - configuration des paramètres liés à la sécurité + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "security.h" + + +#include <i18n.h> + + +#include "security-int.h" +#include "../../../core/global.h" +#include "../../../core/logs.h" +#include "../../../gtkext/helpers.h" + + + +/* ---------------------- GESTION DE L'ENSEMBLE DES PARAMETRES ---------------------- */ + + +/* Procède à l'initialisation de classe des configurations. */ +static void gtk_security_tweak_panel_class_init(GtkSecurityTweakPanelClass *); + +/* Procède à l'initialisation des configurations de sécurité. */ +static void gtk_security_tweak_panel_init(GtkSecurityTweakPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_security_tweak_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_security_tweak_panel_finalize(GObject *); + + + +/* ---------------------- CONSERVATION SECURISEE DE PARAMETRES ---------------------- */ + + +/* Initalise la partie relative au stockage sécurisé. */ +static void gtk_security_tweak_panel_init_secret_storage(GtkSecurityTweakPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_security_tweak_panel_dispose_secret_storage(GtkSecurityTweakPanel *); + +/* Note le changement de verrouillage du stockage sécurisé. */ +static void gtk_security_tweak_panel_on_secret_storage_lock_update(GSecretStorage *, GtkSecurityTweakPanel *); + +/* Réagit à un changement dans l'édition d'un mot de passe. */ +static void gtk_security_tweak_panel_on_new_passwords_changed(GtkEditable *, GtkSecurityTweakPanel *); + +/* Réagit à une demande de création de mot de passe. */ +static void gtk_security_tweak_panel_on_set_password_clicked(GtkButton *, GtkSecurityTweakPanel *); + +/* Réagit à une demande de changement de mot de passe. */ +static void gtk_security_tweak_panel_on_change_password_clicked(GtkButton *, GtkSecurityTweakPanel *); + +/* Réagit à une demande de suppression de mot de passe. */ +static void gtk_security_tweak_panel_on_remove_password_clicked(GtkButton *, GtkSecurityTweakPanel *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION DE L'ENSEMBLE DES PARAMETRES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type du composant d'édition des paramètres de sécurité. */ +G_DEFINE_TYPE(GtkSecurityTweakPanel, gtk_security_tweak_panel, GTK_TYPE_BOX); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de classe des configurations. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_class_init(GtkSecurityTweakPanelClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_security_tweak_panel_dispose; + object->finalize = gtk_security_tweak_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/prefs/security.ui"); + + /* Stockage sécurisé */ + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_new_passwords_changed)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_set_password_clicked)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_change_password_clicked)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_remove_password_clicked)); + + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, current_primary_passwd); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_lbl); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_2); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_2_lbl); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, set_password); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, change_password); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, remove_password); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation des configurations de sécurité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_init(GtkSecurityTweakPanel *panel) +{ + gtk_widget_init_template(GTK_WIDGET(panel)); + + gtk_security_tweak_panel_init_secret_storage(panel); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_dispose(GObject *object) +{ + GtkSecurityTweakPanel *panel; /* Version spécialisée */ + + panel = GTK_SECURITY_TWEAK_PANEL(object); + + gtk_security_tweak_panel_dispose_secret_storage(panel); + + gtk_widget_dispose_template(GTK_WIDGET(panel), GTK_TYPE_SECURITY_TWEAK_PANEL); + + G_OBJECT_CLASS(gtk_security_tweak_panel_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_security_tweak_panel_parent_class)->finalize(object); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONSERVATION SECURISEE DE PARAMETRES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : panel = composant GTK à initialiser. * +* * +* Description : Initalise la partie relative au stockage sécurisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_init_secret_storage(GtkSecurityTweakPanel *panel) +{ + panel->storage = get_secret_storage(); + + g_signal_connect(panel->storage, "lock-update", + G_CALLBACK(gtk_security_tweak_panel_on_secret_storage_lock_update), panel); + + gtk_security_tweak_panel_on_secret_storage_lock_update(panel->storage, panel); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_dispose_secret_storage(GtkSecurityTweakPanel *panel) +{ + if (panel->storage != NULL) + g_signal_handlers_disconnect_by_func(panel->storage, + gtk_security_tweak_panel_on_secret_storage_lock_update, panel); + + g_clear_object(&panel->storage); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gardien des secrets impliqué. * +* panel = support de la partie relative au stockage sécurisé.* +* * +* Description : Note le changement de verrouillage du stockage sécurisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_secret_storage_lock_update(GSecretStorage *storage, GtkSecurityTweakPanel *panel) +{ + bool has_key; /* Existence d'une clef ? */ + + has_key = g_secret_storage_has_key(panel->storage); + + gtk_widget_set_visible(panel->new_primary_passwd, has_key); + gtk_widget_set_visible(panel->new_primary_passwd_lbl, has_key); + + gtk_widget_set_visible(panel->new_primary_passwd_2, has_key); + gtk_widget_set_visible(panel->new_primary_passwd_2_lbl, has_key); + + gtk_widget_set_visible(panel->set_password, !has_key); + gtk_widget_set_visible(panel->change_password, has_key); + gtk_widget_set_visible(panel->remove_password, has_key); + +} + + +/****************************************************************************** +* * +* Paramètres : editable = zone de saisie bouton GTK concerné par l'appel. * +* panel = support de la partie liée au stockage sécurisé. * +* * +* Description : Réagit à un changement dans l'édition d'un mot de passe. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_new_passwords_changed(GtkEditable *editable, GtkSecurityTweakPanel *panel) +{ + const char *new_passwd; /* Nouveau mot de passe #1 */ + const char *new_passwd_2; /* Nouveau mot de passe #2 */ + bool status; /* Bilan de l'opération */ + + new_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd)); + + new_passwd_2 = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd_2)); + + status = (strcmp(new_passwd, new_passwd_2) == 0); + + gtk_widget_set_sensitive(panel->change_password, status); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton GTK concerné par l'appel. * +* panel = support de la partie relative au stockage sécurisé. * +* * +* Description : Réagit à une demande de création de mot de passe. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_set_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel) +{ + const char *passwd; /* Mot de passe à considérer */ + bool status; /* Bilan de l'opération */ + + passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd)); + + status = g_secret_storage_set_password(panel->storage, passwd); + + log_variadic_message(LMT_INFO, _("Setting password for secret storage: %s"), + status ? _("success") : _("failed")); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton GTK concerné par l'appel. * +* panel = support de la partie relative au stockage sécurisé. * +* * +* Description : Réagit à une demande de changement de mot de passe. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_change_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel) +{ + const char *old_passwd; /* Ancien mot de passe */ + const char *new_passwd; /* Nouveau mot de passe */ + bool status; /* Bilan de l'opération */ + + old_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd)); + + new_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd)); + + status = g_secret_storage_change_password(panel->storage, old_passwd, new_passwd); + + log_variadic_message(LMT_INFO, _("Changed password for secret storage: %s"), + status ? _("success") : _("failed")); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton GTK concerné par l'appel. * +* panel = support de la partie relative au stockage sécurisé. * +* * +* Description : Réagit à une demande de suppression de mot de passe. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_remove_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel) +{ + const char *passwd; /* Mot de passe à considérer */ + bool status; /* Bilan de l'opération */ + + passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd)); + + status = g_secret_storage_remove_password(panel->storage, passwd); + + log_variadic_message(LMT_INFO, _("Removed password for secret storage: %s"), + status ? _("success") : _("failed")); + +} diff --git a/src/gui/dialogs/prefs/security.h b/src/gui/dialogs/prefs/security.h new file mode 100644 index 0000000..206a123 --- /dev/null +++ b/src/gui/dialogs/prefs/security.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * security.h - prototypes pour la configuration des paramètres liés à la sécurité + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_DIALOGS_PREFS_SECURITY_H +#define _GUI_DIALOGS_PREFS_SECURITY_H + + +#include <gtk/gtk.h> + + +#include "../../../glibext/helpers.h" + + + +#define GTK_TYPE_SECURITY_TWEAK_PANEL (gtk_security_tweak_panel_get_type()) + +DECLARE_GTYPE(GtkSecurityTweakPanel, gtk_security_tweak_panel, GTK, SECURITY_TWEAK_PANEL); + + + +#endif /* _GUI_DIALOGS_PREFS_SECURITY_H */ diff --git a/src/gui/dialogs/prefs/security.ui b/src/gui/dialogs/prefs/security.ui new file mode 100644 index 0000000..ccf2d39 --- /dev/null +++ b/src/gui/dialogs/prefs/security.ui @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkSecurityTweakPanel" parent="GtkBox"> + + <property name="orientation">vertical</property> + + <!-- Conservation de paramètres sécurisée --> + <child> + <object class="GtkGrid"> + <property name="margin-start">20</property> + <property name="margin-end">20</property> + <property name="margin-top">20</property> + <property name="margin-bottom">20</property> + <property name="row-spacing">10</property> + <property name="column-spacing">10</property> + + <child> + <object class="GtkLabel"> + <property name="label">Secret storage</property> + <property name="use-markup">true</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="column-span">2</property> + </layout> + <style> + <class name="heading"/> + </style> + </object> + </child> + + <child> + <object class="GtkLabel"> + <property name="label">Configuration may handle sensitive data (such as passwords or API keys) which can be saved in plain text or stored encrypted using a Primary Password. + +If encryption is activated, entering the Primary Password will be asked on need once per session.</property> + <property name="wrap">true</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + <property name="column-span">2</property> + </layout> + <style> + <class name="dim-label"/> + </style> + </object> + </child> + + <!-- Mot de passe courant --> + + <child> + <object class="GtkLabel"> + <property name="label">Enter current password:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> + </object> + </child> + + <child> + <object class="GtkPasswordEntry" id="current_primary_passwd"> + <property name="hexpand">true</property> + <property name="show-peek-icon">true</property> + <layout> + <property name="column">1</property> + <property name="row">2</property> + </layout> + </object> + </child> + + <!-- Nouveau mot de passe --> + + <child> + <object class="GtkLabel" id="new_primary_passwd_lbl"> + <property name="label">Enter new password:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">3</property> + </layout> + </object> + </child> + + <child> + <object class="GtkPasswordEntry" id="new_primary_passwd"> + <property name="hexpand">true</property> + <property name="show-peek-icon">true</property> + <signal name="changed" handler="gtk_security_tweak_panel_on_new_passwords_changed"/> + <layout> + <property name="column">1</property> + <property name="row">3</property> + </layout> + </object> + </child> + + <!-- Confirmation de mot de passe --> + + <child> + <object class="GtkLabel" id="new_primary_passwd_2_lbl"> + <property name="label">Re-enter new password:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">4</property> + </layout> + </object> + </child> + + <child> + <object class="GtkPasswordEntry" id="new_primary_passwd_2"> + <property name="hexpand">true</property> + <property name="show-peek-icon">true</property> + <signal name="changed" handler="gtk_security_tweak_panel_on_new_passwords_changed"/> + <layout> + <property name="column">1</property> + <property name="row">4</property> + </layout> + </object> + </child> + + <!-- Boutons de contrôle --> + + <child> + <object class="GtkBox"> + <property name="halign">center</property> + <property name="homogeneous">true</property> + <property name="spacing">8</property> + <layout> + <property name="column">0</property> + <property name="row">5</property> + <property name="column-span">2</property> + </layout> + + <child> + <object class="GtkButton" id="set_password"> + <property name="label">Set</property> + <signal name="clicked" handler="gtk_security_tweak_panel_on_set_password_clicked"/> + </object> + </child> + + <child> + <object class="GtkButton" id="change_password"> + <property name="label">Change</property> + <signal name="clicked" handler="gtk_security_tweak_panel_on_change_password_clicked"/> + </object> + </child> + + <child> + <object class="GtkButton" id="remove_password"> + <property name="label">Remove</property> + <signal name="clicked" handler="gtk_security_tweak_panel_on_remove_password_clicked"/> + <style> + <class name="destructive-action"/> + </style> + </object> + </child> + + </object> + </child> + + </object> + </child> + + </template> +</interface> diff --git a/src/gui/gresource.xml b/src/gui/gresource.xml index 011281d..91d9bc9 100644 --- a/src/gui/gresource.xml +++ b/src/gui/gresource.xml @@ -1,6 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <gresources> - <gresource prefix="/org/chrysalide/gui"> - <file compressed="true">editor.ui</file> + <gresource prefix="/re/chrysalide/framework/gui"> + <file compressed="true">style.css</file> + <file compressed="true">window.ui</file> + </gresource> + <gresource prefix="/re/chrysalide/framework/images"> + <file compressed="true" alias="chrysalide-logo.svg">../../pixmaps/chrysalide-logo.svg</file> </gresource> </gresources> diff --git a/src/gui/panels/Makefile.am b/src/gui/panels/Makefile.am index 83e173b..ecff6c7 100644 --- a/src/gui/panels/Makefile.am +++ b/src/gui/panels/Makefile.am @@ -3,7 +3,7 @@ DEFAULT_INCLUDES = -idirafter. -I$(top_builddir) BUILT_SOURCES = resources.h resources.c -noinst_LTLIBRARIES = libguipanels.la +noinst_LTLIBRARIES = libguipanels4.la # libguipanels.la UI_FILES = \ bintree.ui \ @@ -11,11 +11,9 @@ UI_FILES = \ errors.ui \ glance.ui \ history.ui \ - log.ui \ regedit.ui \ strings.ui \ - symbols.ui \ - welcome.ui + symbols.ui libguipanels_la_SOURCES = \ bintree.h bintree.c \ @@ -23,25 +21,50 @@ libguipanels_la_SOURCES = \ errors.h errors.c \ glance.h glance.c \ history.h history.c \ - log.h log.c \ regedit.h regedit.c \ resources.h resources.c \ strings.h strings.c \ symbols.h symbols.c \ updating-int.h \ updating.h updating.c \ - view.h view.c \ - welcome.h welcome.c + view.h view.c libguipanels_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) +IMG_PATH = ../../../data/images + +RES_FILES = \ + binary.ui \ + binary-params.ui \ + $(IMG_PATH)/binfile-symbolic.svg \ + logs.ui \ + logs-col-icon.ui \ + logs-col-message.ui \ + welcome.ui \ + welcome-hints.txt \ + $(IMG_PATH)/tipoftheday-symbolic.svg + +libguipanels4_la_SOURCES = \ + binary-int.h \ + binary.h binary.c \ + binary-params-int.h \ + binary-params.h binary-params.c \ + logs-int.h \ + logs.h logs.c \ + resources.h resources.c \ + welcome-int.h \ + welcome.h welcome.c + +libguipanels4_la_CFLAGS = $(LIBGTK4_CFLAGS) + + devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) dev_HEADERS = $(libguipanels_la_SOURCES:%c=) -resources.c: gresource.xml $(UI_FILES) +resources.c: gresource.xml $(RES_FILES) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_panels gresource.xml resources.h: gresource.xml @@ -50,4 +73,4 @@ resources.h: gresource.xml CLEANFILES = resources.h resources.c -EXTRA_DIST = gresource.xml $(UI_FILES) +EXTRA_DIST = gresource.xml $(RES_FILES) diff --git a/src/gui/panels/binary-int.h b/src/gui/panels/binary-int.h new file mode 100644 index 0000000..5116f5c --- /dev/null +++ b/src/gui/panels/binary-int.h @@ -0,0 +1,52 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * binary-int.h - prototypes internes pour le panneau d'affichage de contenus d'un binaire, bruts ou non + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _GUI_PANELS_BINARY_INT_H +#define _GUI_PANELS_BINARY_INT_H + + +#include "binary.h" +#include "../../gtkext/panel-int.h" + + + +/* Panneau d'accueil par défaut (instance) */ +struct _GtkBinaryPanel +{ + GtkTiledPanel parent; /* A laisser en premier */ + + GtkScrolledWindow *hex_scroll; /* Défilement pour contenu #0 */ + +}; + +/* Panneau d'accueil par défaut (classe) */ +struct _GtkBinaryPanelClass +{ + GtkTiledPanelClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GUI_PANELS_BINARY_INT_H */ diff --git a/src/gui/panels/binary-params-int.h b/src/gui/panels/binary-params-int.h new file mode 100644 index 0000000..0fbef24 --- /dev/null +++ b/src/gui/panels/binary-params-int.h @@ -0,0 +1,50 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * binary-params-int.h - définitions internes pour l'édition des paramètres initiaux d'un chargement de binaire + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_PANELS_BINARY_PARAMS_INT_H +#define _GUI_PANELS_BINARY_PARAMS_INT_H + + +#include "binary-params.h" + + + +/* Composant pour les paramètres de chargement d'un binaire (instance) */ +struct _GtkBinaryParameters +{ + GtkGrid parent; /* A laisser en premier */ + + GtkEntry *filename; /* CHemin d'un binaire */ + +}; + +/* Composant pour les paramètres de chargement d'un binaire (classe) */ +struct _GtkBinaryParametersClass +{ + GtkGridClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GUI_PANELS_BINARY_PARAMS_INT_H */ diff --git a/src/gui/panels/binary-params.c b/src/gui/panels/binary-params.c new file mode 100644 index 0000000..1059761 --- /dev/null +++ b/src/gui/panels/binary-params.c @@ -0,0 +1,178 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * binary-params.c - édition des paramètres initiaux d'un chargement de binaire + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "binary-params.h" + + +#include "binary.h" +#include "binary-params-int.h" +#include "../window.h" +#include "../../analysis/contents/file.h" +#include "../../gtkext/helpers.h" + + + +/* Initialise la classe des composants d'édition de paramètres. */ +static void gtk_binary_parameters_class_init(GtkBinaryParametersClass *); + +/* Initialise une instance de composant d'édition de paramètres. */ +static void gtk_binary_parameters_init(GtkBinaryParameters *); + +/* Supprime toutes les références externes. */ +static void gtk_binary_parameters_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_binary_parameters_finalize(GObject *); + +/* Ouvre une boîte de dialogue pour récupérer un fichier. */ +static void gtk_binary_parameters_on_new_file_entry_icon_release(GtkEntry *, GtkEntryIconPosition, GtkBinaryParameters *); + + + +/* Détermine le type du composant d'édition des paramètres de chargement. */ +G_DEFINE_TYPE(GtkBinaryParameters, gtk_binary_parameters, GTK_TYPE_GRID); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des composants d'édition de paramètres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_class_init(GtkBinaryParametersClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_binary_parameters_dispose; + object->finalize = gtk_binary_parameters_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/binary-params.ui"); + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_binary_parameters_on_new_file_entry_icon_release)); + + gtk_widget_class_bind_template_child(widget, GtkBinaryParameters, filename); + +} + + +/****************************************************************************** +* * +* Paramètres : params = composant GTK à initialiser. * +* * +* Description : Initialise une instance de composant d'édition de paramètres.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_init(GtkBinaryParameters *params) +{ + gtk_widget_init_template(GTK_WIDGET(params)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_BINARY_PARAMETERS); + + G_OBJECT_CLASS(gtk_binary_parameters_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_binary_parameters_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : entry = zone de saisie concernée par l'appel. * +* icon_pos = position de l'icone incrustée dans la zone. * +* params = composant d'édition des paramètres. * +* * +* Description : Ouvre une boîte de dialogue pour récupérer un fichier. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_on_new_file_entry_icon_release(GtkEntry *entry, GtkEntryIconPosition icon_pos, GtkBinaryParameters *params) +{ + GtkRoot *root; /* Racine du composant */ + GBinContent *content; /* Contenu binaire à afficher */ + GtkTiledPanel *tiled; /* Panneau d'affichage complet */ + + root = gtk_widget_get_root(GTK_WIDGET(entry)); + + content = g_file_content_new("/bin/id"); + + tiled = gtk_binary_panel_new_for_content(content); + + unref_object(content); + + gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled); + +} diff --git a/src/gui/panels/binary-params.h b/src/gui/panels/binary-params.h new file mode 100644 index 0000000..450da25 --- /dev/null +++ b/src/gui/panels/binary-params.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * binary-params.h - prototypes pour l'édition des paramètres initiaux d'un chargement de binaire + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_PANELS_BINARY_PARAMS_H +#define _GUI_PANELS_BINARY_PARAMS_H + + +#include <gtk/gtk.h> + + +#include "../../glibext/helpers.h" + + + +#define GTK_TYPE_BINARY_PARAMETERS (gtk_binary_parameters_get_type()) + +DECLARE_GTYPE(GtkBinaryParameters, gtk_binary_parameters, GTK, BINARY_PARAMETERS); + + + +#endif /* _GUI_PANELS_BINARY_PARAMS_H */ diff --git a/src/gui/panels/binary-params.ui b/src/gui/panels/binary-params.ui new file mode 100644 index 0000000..dcbaf7c --- /dev/null +++ b/src/gui/panels/binary-params.ui @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkBinaryParameters" parent="GtkGrid"> + <property name="margin-bottom">12</property> + <property name="margin-end">12</property> + <property name="margin-start">12</property> + <property name="margin-top">12</property> + <property name="column-spacing">12</property> + <property name="row-spacing">8</property> + + <child> + <object class="GtkLabel"> + <property name="label">Load and analyze a new file:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + + <child> + <object class="GtkEntry" id="filename"> + <property name="secondary-icon-name">document-open-symbolic</property> + <property name="placeholder-text">File location</property> + <property name="hexpand">TRUE</property> + <property name="hexpand-set">TRUE</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + <style> + <class name="background"/> + </style> + <signal name="icon-release" handler="gtk_binary_parameters_on_new_file_entry_icon_release"/> + </object> + </child> + + </template> + +</interface> diff --git a/src/gui/panels/binary.c b/src/gui/panels/binary.c new file mode 100644 index 0000000..f58c06b --- /dev/null +++ b/src/gui/panels/binary.c @@ -0,0 +1,195 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * binary.c - panneau d'affichage de contenus d'un binaire, bruts ou non + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "binary.h" + + +#include "binary-int.h" +#include "../../gtkext/helpers.h" +#include "../../gtkext/hexview.h" + + + +/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ + + +/* Initialise la classe des panneaux pour binaires. */ +static void gtk_binary_panel_class_init(GtkBinaryPanelClass *); + +/* Initialise une instance de panneau pour binaire. */ +static void gtk_binary_panel_init(GtkBinaryPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_binary_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_binary_panel_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + + + + + +/* ---------------------------------------------------------------------------------- */ +/* COEUR D'UN PANNEAU D'AFFICHAGE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un panneau d'affichage de contenus d'un binaire. */ +G_DEFINE_TYPE(GtkBinaryPanel, gtk_binary_panel, GTK_TYPE_TILED_PANEL); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des panneaux pour binaires. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_panel_class_init(GtkBinaryPanelClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_binary_panel_dispose; + object->finalize = gtk_binary_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/binary.ui"); + + gtk_widget_class_bind_template_child(widget, GtkBinaryPanel, hex_scroll); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance à initialiser. * +* * +* Description : Initialise une instance de panneau pour binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_panel_init(GtkBinaryPanel *panel) +{ + gtk_widget_init_template(GTK_WIDGET(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_panel_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_BINARY_PANEL); + + G_OBJECT_CLASS(gtk_binary_panel_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_panel_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_binary_panel_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : content = contenu brut à exposer. * +* * +* Description : Crée une nouvelle instance de panneau pour binaire. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkTiledPanel *gtk_binary_panel_new_for_content(GBinContent *content) +{ + GtkTiledPanel *result; /* Instance à retourner */ + GtkHexView *view; /* Composant d'affichage */ + + result = g_object_new(GTK_TYPE_BINARY_PANEL, NULL); + + view = gtk_hex_view_new(content); + gtk_scrolled_window_set_child(GTK_BINARY_PANEL(result)->hex_scroll, GTK_WIDGET(view)); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + + + + + + + diff --git a/src/gui/panels/binary.h b/src/gui/panels/binary.h new file mode 100644 index 0000000..26f8a7d --- /dev/null +++ b/src/gui/panels/binary.h @@ -0,0 +1,48 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * binary.h - prototypes pour le panneau d'accueil par défaut + * + * Copyright (C) 2012-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _GUI_PANELS_BINARY_H +#define _GUI_PANELS_BINARY_H + + +#include <gtk/gtk.h> + + +#include "../../analysis/content.h" +#include "../../glibext/helpers.h" +#include "../../gtkext/panel.h" + + + +#define GTK_TYPE_BINARY_PANEL (gtk_binary_panel_get_type()) + +DECLARE_GTYPE(GtkBinaryPanel, gtk_binary_panel, GTK, BINARY_PANEL); + + +/* Crée une nouvelle instance de panneau pour binaire. */ +GtkTiledPanel *gtk_binary_panel_new_for_content(GBinContent *); + + + +#endif /* _GUI_PANELS_BINARY_H */ diff --git a/src/gui/panels/binary.ui b/src/gui/panels/binary.ui new file mode 100644 index 0000000..a34c409 --- /dev/null +++ b/src/gui/panels/binary.ui @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkBinaryPanel" parent="GtkTiledPanel"> + <child> + <object class="GtkScrolledWindow" id="hex_scroll"> + <property name="hscrollbar-policy">automatic</property> + <property name="vscrollbar-policy">automatic</property> + <property name="hexpand">TRUE</property> + <property name="vexpand">TRUE</property> + <property name="has-frame">0</property> + </object> + </child> + </template> + +</interface> diff --git a/src/gui/panels/gresource.xml b/src/gui/panels/gresource.xml index d996ef1..2765b25 100644 --- a/src/gui/panels/gresource.xml +++ b/src/gui/panels/gresource.xml @@ -1,20 +1,16 @@ <?xml version="1.0" encoding="UTF-8"?> <gresources> - <gresource prefix="/org/chrysalide/gui/panels"> - <file compressed="true">../../../pixmaps/tbutton_list_view.png</file> - <file compressed="true">../../../pixmaps/tbutton_tree_view.png</file> - <file compressed="true">../../../pixmaps/tbutton_collapse.png</file> - <file compressed="true">../../../pixmaps/tbutton_expand.png</file> - <file compressed="true">../../../pixmaps/symbol_class_classic.png</file> - <file compressed="true">bintree.ui</file> - <file compressed="true">bookmarks.ui</file> - <file compressed="true">errors.ui</file> - <file compressed="true">glance.ui</file> - <file compressed="true">history.ui</file> - <file compressed="true">log.ui</file> - <file compressed="true">regedit.ui</file> - <file compressed="true">strings.ui</file> - <file compressed="true">symbols.ui</file> + <gresource prefix="/re/chrysalide/framework/gui/panels"> + <file compressed="true">binary.ui</file> + <file compressed="true">binary-params.ui</file> + <file compressed="true">logs.ui</file> + <file compressed="true">logs-col-icon.ui</file> + <file compressed="true">logs-col-message.ui</file> <file compressed="true">welcome.ui</file> + <file compressed="true">welcome-hints.txt</file> + </gresource> + <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions"> + <file compressed="true" alias="binfile-symbolic.svg">../../../data/images/binfile-symbolic.svg</file> + <file compressed="true" alias="tipoftheday-symbolic.svg">../../../data/images/tipoftheday-symbolic.svg</file> </gresource> </gresources> diff --git a/src/gui/panels/log.c b/src/gui/panels/log.c deleted file mode 100644 index d11fbd2..0000000 --- a/src/gui/panels/log.c +++ /dev/null @@ -1,451 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * log.c - panneau d'affichage des messages système - * - * Copyright (C) 2012-2019 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "log.h" - - -#include <malloc.h> -#include <stdarg.h> -#include <stdio.h> -#include <string.h> -#include <gtk/gtk.h> - - -#include "../panel-int.h" -#include "../core/panels.h" -#include "../../gtkext/easygtk.h" -#include "../../gtkext/named.h" - - - -/* Colonnes de la liste des messages */ -typedef enum _LogColumn -{ - LGC_PICTURE, /* Image de représentation */ - LGC_STRING, /* Chaîne de caractères */ - - LGC_COUNT /* Nombre de colonnes */ - -} LogColumn; - - -/* Paramètres à transmettre pour un affichage */ -typedef struct _log_data -{ - GPanelItem *item; /* Intermédiaire mis en place */ - LogMessageType type; /* Type de message à afficher */ - char *msg; /* Contenu du message */ - -} log_data; - - -/* Panneau d'accueil (instance) */ -struct _GLogPanel -{ - GPanelItem parent; /* A laisser en premier */ - -}; - - -/* Panneau d'accueil (classe) */ -struct _GLogPanelClass -{ - GPanelItemClass parent; /* A laisser en premier */ - -}; - - -/* Initialise la classe des panneaux d'affichage des messages. */ -static void g_log_panel_class_init(GLogPanelClass *); - -/* Initialise une instance de panneau d'affichage des messages. */ -static void g_log_panel_init(GLogPanel *); - -/* Supprime toutes les références externes. */ -static void g_log_panel_dispose(GLogPanel *); - -/* Procède à la libération totale de la mémoire. */ -static void g_log_panel_finalize(GLogPanel *); - -/* Fournit le nom interne attribué à l'élément réactif. */ -static char *g_log_panel_class_get_key(const GLogPanelClass *); - -/* Fournit une indication sur la personnalité du panneau. */ -static PanelItemPersonality g_log_panel_class_get_personality(const GLogPanelClass *); - -/* Indique le chemin initial de la localisation d'un panneau. */ -static char *g_log_panel_class_get_path(const GLogPanelClass *); - -/* Indique la définition d'un éventuel raccourci clavier. */ -static char *g_log_panel_class_get_key_bindings(const GLogPanelClass *); - -/* Affiche un message dans le journal des messages système. */ -static gboolean log_message(log_data *); - - - -/* Indique le type défini pour un panneau d'affichage de messages. */ -G_DEFINE_TYPE(GLogPanel, g_log_panel, G_TYPE_PANEL_ITEM); - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* * -* Description : Initialise la classe des panneaux d'affichage des messages. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_log_panel_class_init(GLogPanelClass *class) -{ - GObjectClass *object; /* Autre version de la classe */ - GEditorItemClass *item; /* Encore une autre vision... */ - GPanelItemClass *panel; /* Version parente de la classe*/ - - object = G_OBJECT_CLASS(class); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_log_panel_dispose; - object->finalize = (GObjectFinalizeFunc)g_log_panel_finalize; - - item = G_EDITOR_ITEM_CLASS(class); - - item->get_key = (get_item_key_fc)g_log_panel_class_get_key; - - panel = G_PANEL_ITEM_CLASS(class); - - panel->get_personality = (get_panel_personality_fc)g_log_panel_class_get_personality; - panel->get_path = (get_panel_path_fc)g_log_panel_class_get_path; - panel->get_bindings = (get_panel_bindings_fc)g_log_panel_class_get_key_bindings; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance à initialiser. * -* * -* Description : Initialise une instance de panneau d'affichage des messages. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_log_panel_init(GLogPanel *panel) -{ - GPanelItem *pitem; /* Version parente du panneau */ - - /* Eléments de base */ - - pitem = G_PANEL_ITEM(panel); - - pitem->widget = G_NAMED_WIDGET(gtk_built_named_widget_new_for_panel(_("Messages"), - _("Misc information"), - PANEL_LOG_ID)); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_log_panel_dispose(GLogPanel *panel) -{ - G_OBJECT_CLASS(g_log_panel_parent_class)->dispose(G_OBJECT(panel)); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_log_panel_finalize(GLogPanel *panel) -{ - G_OBJECT_CLASS(g_log_panel_parent_class)->finalize(G_OBJECT(panel)); - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Fournit le nom interne attribué à l'élément réactif. * -* * -* Retour : Désignation (courte) de l'élément de l'éditeur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *g_log_panel_class_get_key(const GLogPanelClass *class) -{ - char *result; /* Description à renvoyer */ - - result = strdup(PANEL_LOG_ID); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Fournit une indication sur la personnalité du panneau. * -* * -* Retour : Identifiant lié à la nature unique du panneau. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PanelItemPersonality g_log_panel_class_get_personality(const GLogPanelClass *class) -{ - PanelItemPersonality result; /* Personnalité à retourner */ - - result = PIP_PERSISTENT_SINGLETON; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Indique le chemin initial de la localisation d'un panneau. * -* * -* Retour : Chemin fixé associé à la position initiale. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *g_log_panel_class_get_path(const GLogPanelClass *class) -{ - char *result; /* Emplacement à retourner */ - - result = strdup("Ms"); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Indique la définition d'un éventuel raccourci clavier. * -* * -* Retour : Description d'un raccourci ou NULL si aucun de défini. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *g_log_panel_class_get_key_bindings(const GLogPanelClass *class) -{ - char *result; /* Emplacement à retourner */ - - result = strdup("<Shift>F1"); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée un panneau d'affichage des messages système. * -* * -* Retour : Adresse de la structure mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPanelItem *g_log_panel_new(void) -{ - GPanelItem *result; /* Structure à retourner */ - - result = g_object_new(G_TYPE_LOG_PANEL, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* type = espèce du message à ajouter. * -* msg = message à faire apparaître à l'écran. * -* * -* Description : Affiche un message dans le journal des messages système. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_log_panel_add_message(GLogPanel *panel, LogMessageType type, const char *msg) -{ - log_data *data; /* Paramètres à joindre */ - - data = calloc(1, sizeof(log_data)); - - data->item = G_PANEL_ITEM(panel); - data->type = type; - data->msg = strdup(msg); - - g_object_ref(G_OBJECT(data->item)); - - g_main_context_invoke(NULL, (GSourceFunc)log_message, data); - -} - - -/****************************************************************************** -* * -* Paramètres : data = paramètres destinés à l'affichage d'un message. * -* * -* Description : Affiche un message dans le journal des messages système. * -* * -* Retour : - * -* * -* Remarques : Cette fonction, et c'est tout son intérêt, est toujours * -* exécutée dans le contexte GTK principal. * -* * -******************************************************************************/ - -static gboolean log_message(log_data *data) -{ - GtkBuilder *builder; /* Constructeur utilisé */ - GtkListStore *store; /* Modèle de gestion */ - GtkTreeIter iter; /* Point d'insertion */ - GtkTreeView *treeview; /* Affichage de la liste */ - - builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(data->item)->widget)); - - /* Mise en place du message */ - - store = GTK_LIST_STORE(gtk_builder_get_object(builder, "store")); - - gtk_list_store_append(store, &iter); - - switch (data->type) - { - case LMT_INFO: - gtk_list_store_set(store, &iter, - LGC_PICTURE, "gtk-info", - LGC_STRING, data->msg, - -1); - break; - - case LMT_PROCESS: - gtk_list_store_set(store, &iter, - LGC_PICTURE, "gtk-execute", - LGC_STRING, data->msg, - -1); - break; - - case LMT_WARNING: - gtk_list_store_set(store, &iter, - LGC_PICTURE, "gtk-dialog-warning", - LGC_STRING, data->msg, - -1); - break; - - case LMT_BAD_BINARY: - gtk_list_store_set(store, &iter, - LGC_PICTURE, "gtk-dialog-warning", - LGC_STRING, data->msg, - -1); - break; - - case LMT_ERROR: - case LMT_EXT_ERROR: - gtk_list_store_set(store, &iter, - LGC_PICTURE, "gtk-dialog-error", - LGC_STRING, data->msg, - -1); - break; - - default: - gtk_list_store_set(store, &iter, - LGC_STRING, data->msg, - -1); - break; - - } - - /* Défilement pour pointer à l'affichage */ - - treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); - - scroll_to_treeview_iter(treeview, GTK_TREE_MODEL(store), &iter); - - g_object_unref(G_OBJECT(builder)); - - /* Nettoyage de la mémoire */ - - g_object_unref(G_OBJECT(data->item)); - - free(data->msg); - - free(data); - - return G_SOURCE_REMOVE; - -} diff --git a/src/gui/panels/log.h b/src/gui/panels/log.h deleted file mode 100644 index 4d155a2..0000000 --- a/src/gui/panels/log.h +++ /dev/null @@ -1,67 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * log.h - prototypes pour le panneau d'affichage des messages système - * - * Copyright (C) 2012-2019 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#ifndef _GUI_PANELS_LOG_H -#define _GUI_PANELS_LOG_H - - -#include <i18n.h> - - -#include "../panel.h" -#include "../../core/logs.h" - - - -#define PANEL_LOG_ID "log" - - -#define G_TYPE_LOG_PANEL g_log_panel_get_type() -#define G_LOG_PANEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_log_panel_get_type(), GLogPanel)) -#define G_IS_LOG_PANEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_log_panel_get_type())) -#define G_LOG_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_LOG_PANEL, GLogPanelClass)) -#define G_IS_LOG_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_LOG_PANEL)) -#define G_LOG_PANEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_LOG_PANEL, GLogPanelClass)) - - -/* Panneau d'affichage de messages (instance) */ -typedef struct _GLogPanel GLogPanel; - -/* Panneau d'affichage de messages (classe) */ -typedef struct _GLogPanelClass GLogPanelClass; - - - -/* Indique le type défini pour un panneau d'affichage de messages. */ -GType g_log_panel_get_type(void); - -/* Crée un panneau d'affichage des messages système. */ -GPanelItem *g_log_panel_new(void); - -/* Affiche un message dans le journal des messages système. */ -void g_log_panel_add_message(GLogPanel *, LogMessageType, const char *); - - - -#endif /* _GUI_PANELS_LOG_H */ diff --git a/src/gui/panels/log.ui b/src/gui/panels/log.ui deleted file mode 100644 index 4ffe96c..0000000 --- a/src/gui/panels/log.ui +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.21.0 --> -<interface> - <requires lib="gtk+" version="3.20"/> - <object class="GtkListStore" id="store"> - <columns> - <!-- column-name picture --> - <column type="gchararray"/> - <!-- column-name string --> - <column type="gchararray"/> - </columns> - </object> - <object class="GtkOffscreenWindow"> - <property name="can_focus">False</property> - <child> - <object class="GtkScrolledWindow" id="box"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="shadow_type">in</property> - <child> - <object class="GtkTreeView" id="treeview"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="model">store</property> - <property name="headers_visible">False</property> - <child internal-child="selection"> - <object class="GtkTreeSelection"/> - </child> - <child> - <object class="GtkTreeViewColumn"> - <property name="title" translatable="yes">picture</property> - <child> - <object class="GtkCellRendererPixbuf"/> - <attributes> - <attribute name="stock-id">0</attribute> - </attributes> - </child> - </object> - </child> - <child> - <object class="GtkTreeViewColumn"> - <property name="title" translatable="yes">string</property> - <child> - <object class="GtkCellRendererText"/> - <attributes> - <attribute name="markup">1</attribute> - </attributes> - </child> - </object> - </child> - </object> - </child> - </object> - </child> - <child type="titlebar"> - <placeholder/> - </child> - </object> -</interface> diff --git a/src/gui/panels/logs-col-icon.ui b/src/gui/panels/logs-col-icon.ui new file mode 100644 index 0000000..6463e84 --- /dev/null +++ b/src/gui/panels/logs-col-icon.ui @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkListItem"> + <property name="child"> + <object class="GtkImage"> + <binding name="icon-name"> + <lookup name="icon-name" type="GLogEntry"> + <lookup name="item">GtkListItem</lookup> + </lookup> + </binding> + </object> + </property> + + </template> + +</interface> diff --git a/src/gui/panels/logs-col-message.ui b/src/gui/panels/logs-col-message.ui new file mode 100644 index 0000000..49839e4 --- /dev/null +++ b/src/gui/panels/logs-col-message.ui @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkListItem"> + <property name="child"> + <object class="GtkLabel"> + <property name="xalign">0</property> + <property name="use-markup">true</property> + <binding name="label"> + <lookup name="message" type="GLogEntry"> + <lookup name="item">GtkListItem</lookup> + </lookup> + </binding> + </object> + </property> + + </template> + +</interface> diff --git a/src/gui/panels/logs-int.h b/src/gui/panels/logs-int.h new file mode 100644 index 0000000..692c1b4 --- /dev/null +++ b/src/gui/panels/logs-int.h @@ -0,0 +1,53 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * logs-int.h - prototypes internes pour le panneau d'affichage des messages système + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _GUI_PANELS_LOGS_INT_H +#define _GUI_PANELS_LOGS_INT_H + + +#include "logs.h" +#include "../../gtkext/panel-int.h" + + + +/* Panneau d'affichage de messages (instance) */ +struct _GtkLogsPanel +{ + GtkTiledPanel parent; /* A laisser en premier */ + + GListStore *store; /* Liste des eléments conservés*/ + GtkWidget *list; /* Composant d'affichage */ + +}; + +/* Panneau d'affichage de messages (classe) */ +struct _GtkLogsPanelClass +{ + GtkTiledPanelClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GUI_PANELS_LOGS_INT_H */ diff --git a/src/gui/panels/logs.c b/src/gui/panels/logs.c new file mode 100644 index 0000000..399c4c0 --- /dev/null +++ b/src/gui/panels/logs.c @@ -0,0 +1,227 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * logs.c - panneau d'affichage des messages système + * + * Copyright (C) 2012-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "logs.h" + + +#include <assert.h> +#include <string.h> + + +#include "logs-int.h" +#include "../../gtkext/helpers.h" + + + +/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ + + +/* Initialise la classe des panneaux d'affichage des journaux. */ +static void gtk_logs_panel_class_init(GtkLogsPanelClass *); + +/* Initialise une instance de panneau d'affichage des journaux. */ +static void gtk_logs_panel_init(GtkLogsPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_logs_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_logs_panel_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Indique l'emplacement par défaut pour un affichage. */ +static char *gtk_logs_panel_get_default_path(const GtkTiledPanel *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COEUR D'UN PANNEAU D'AFFICHAGE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un panneau d'accueil. */ +G_DEFINE_TYPE(GtkLogsPanel, gtk_logs_panel, GTK_TYPE_TILED_PANEL); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des panneaux d'affichage des journaux. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_logs_panel_class_init(GtkLogsPanelClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + GtkTiledPanelClass *panel; /* Classe parente */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_logs_panel_dispose; + object->finalize = gtk_logs_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + g_type_ensure(G_TYPE_LOG_ENTRY); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/logs.ui"); + + //gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_logs_panel_on_selected_rows_changed)); + + gtk_widget_class_bind_template_child(widget, GtkLogsPanel, store); + gtk_widget_class_bind_template_child(widget, GtkLogsPanel, list); + + panel = GTK_TILED_PANEL_CLASS(class); + + panel->get_default_path = gtk_logs_panel_get_default_path; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance à initialiser. * +* * +* Description : Initialise une instance de panneau d'affichage des journaux. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_logs_panel_init(GtkLogsPanel *panel) +{ + GtkWidget *headers; /* Composant à cacher */ + + gtk_widget_init_template(GTK_WIDGET(panel)); + + /** + * Retrait des entêtes de colonne de l'affichage. + */ + + headers = gtk_widget_get_first_child(panel->list); + + gtk_widget_set_visible(headers, FALSE); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_logs_panel_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_LOGS_PANEL); + + G_OBJECT_CLASS(gtk_logs_panel_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_logs_panel_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_logs_panel_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance d'objet GLib à traiter. * +* entry = élément de journalisation à intégrer. * +* * +* Description : Affiche un message dans le journal des messages système. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_log_panel_add_message(GtkLogsPanel *panel, GLogEntry *entry) +{ + g_list_store_append(panel->store, entry); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : panel = panneau graphique à consulter. * +* * +* Description : Indique l'emplacement par défaut pour un affichage. * +* * +* Retour : Chemin représenté ou NULL pour l'emplacement "M" par défaut. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *gtk_logs_panel_get_default_path(const GtkTiledPanel *panel) +{ + char *result; /* Chemin à retourner */ + + result = strdup("S"); + + return result; + +} diff --git a/src/gui/panels/logs.h b/src/gui/panels/logs.h new file mode 100644 index 0000000..a8b902b --- /dev/null +++ b/src/gui/panels/logs.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * logs.h - prototypes pour le panneau d'affichage des messages système + * + * Copyright (C) 2012-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _GUI_PANELS_LOGS_H +#define _GUI_PANELS_LOGS_H + + +#include <gtk/gtk.h> + + +#include "../../glibext/helpers.h" +#include "../../glibext/log.h" + + + +#define GTK_TYPE_LOGS_PANEL (gtk_logs_panel_get_type()) + +DECLARE_GTYPE(GtkLogsPanel, gtk_logs_panel, GTK, LOGS_PANEL); + + +/* Affiche un message dans le journal des messages système. */ +void g_log_panel_add_message(GtkLogsPanel *, GLogEntry *); + + + +#endif /* _GUI_PANELS_LOGS_H */ diff --git a/src/gui/panels/logs.ui b/src/gui/panels/logs.ui new file mode 100644 index 0000000..ba920cd --- /dev/null +++ b/src/gui/panels/logs.ui @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <object class="GtkNoSelection" id="noselection"> + <property name="model"> + <object class="GListStore" id="store"> + <property name="item-type">GLogEntry</property> + </object> + </property> + </object> + + <template class="GtkLogsPanel" parent="GtkTiledPanel"> + + <child> + <object class="GtkScrolledWindow"> + <property name="hscrollbar-policy">automatic</property> + <property name="vscrollbar-policy">automatic</property> + <property name="hexpand">true</property> + <property name="vexpand">true</property> + <property name="has-frame">0</property> + + <child> + <object class="GtkColumnView" id="list"> + <property name="vexpand">true</property> + <property name="model">noselection</property> + + <child> + <object class="GtkColumnViewColumn"> + <property name="title"></property> + <property name="factory"> + <object class="GtkBuilderListItemFactory"> + <property name="resource">/re/chrysalide/framework/gui/panels/logs-col-icon.ui</property> + </object> + </property> + </object> + </child> + + <child> + <object class="GtkColumnViewColumn"> + <property name="expand">true</property> + <property name="title">Message</property> + <property name="factory"> + <object class="GtkBuilderListItemFactory"> + <property name="resource">/re/chrysalide/framework/gui/panels/logs-col-message.ui</property> + </object> + </property> + </object> + </child> + + </object> + </child> + + </object> + </child> + + </template> + +</interface> diff --git a/src/gui/panels/welcome-hints.txt b/src/gui/panels/welcome-hints.txt new file mode 100644 index 0000000..a35ea64 --- /dev/null +++ b/src/gui/panels/welcome-hints.txt @@ -0,0 +1,23 @@ +Chrysalide's GUI offers launchers at startup in order to run main activities quickly. + +Once an activity is selected, options get displayed and allow some tunning before starting new processes. + +Such options are usually saved between runs. + + +There is no need to install Chrysalide on your system if you only want to give it a try. + +Just compile the source code and run the program from there. + + +Chrysalide can be used in external Python scripts by setting PYTHONPATH to the directory containing the 'pychrysalide.so' file. For instance: + + cd plugins/pychrysa/.libs/ + export PYTHONPATH=$PWD + +Then run the interpreter suitable to your configuration (debug or release): + + python3-dbg -c 'import pychrysalide ; print(pychrysalide.mod_version())' + + +All the configuration files for Chrysalide are located in $HOME/.config/chrysalide/.
\ No newline at end of file diff --git a/src/gui/panels/welcome-int.h b/src/gui/panels/welcome-int.h new file mode 100644 index 0000000..206bc2c --- /dev/null +++ b/src/gui/panels/welcome-int.h @@ -0,0 +1,61 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * welcome-int.h - prototypes internes pour le panneau d'accueil par défaut + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _GUI_PANELS_WELCOME_INT_H +#define _GUI_PANELS_WELCOME_INT_H + + +#include "welcome.h" +#include "../../gtkext/panel-int.h" + + + +/* Panneau d'accueil par défaut (instance) */ +struct _GtkWelcomePanel +{ + GtkTiledPanel parent; /* A laisser en premier */ + + GtkListBox *list; /* Liste de lanceurs */ + GtkStack *properties; /* Premières propriétés */ + + GtkWidget *def_child; /* Contenu par défaut */ + GtkLabel *hints; /* Affichage d'astuces du jour */ + GtkWidget *other_child; /* Autre contenu, alternatif */ + + gchar **raw_hints; /* Liste d'astuces */ + guint raw_count; /* Taille de cette liste */ + guint cur_hint; /* Position dans le parcours */ + +}; + +/* Panneau d'accueil par défaut (classe) */ +struct _GtkWelcomePanelClass +{ + GtkTiledPanelClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GUI_PANELS_WELCOME_INT_H */ diff --git a/src/gui/panels/welcome.c b/src/gui/panels/welcome.c index 60593d1..6e8763b 100644 --- a/src/gui/panels/welcome.c +++ b/src/gui/panels/welcome.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * welcome.c - panneau d'accueil par défaut * - * Copyright (C) 2012-2019 Cyrille Bagard + * Copyright (C) 2012-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,130 +26,63 @@ #include <assert.h> -#include <malloc.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <i18n.h> - - -#include "../panel-int.h" -#include "../core/global.h" -#include "../../common/cpp.h" -#include "../../common/io.h" -#include "../../common/net.h" +#include "welcome-int.h" +#include "../core/panels.h" #include "../../common/shuffle.h" -#include "../../core/global.h" -#include "../../core/params.h" -#include "../../core/paths.h" -#include "../../gtkext/easygtk.h" -#include "../../gtkext/named.h" - - - -/* Panneau d'accueil par défaut (instance) */ -struct _GWelcomePanel -{ - GPanelItem parent; /* A laisser en premier */ +#include "../../gtkext/helpers.h" - cairo_surface_t *background; /* Fond pour astuces */ - char **tips; /* Liste de toutes les astuces */ - size_t count; /* Quantité d'astuces */ - size_t current; /* Indice de l'astuce courante */ - - bool uorigin; /* Origine de l'affichage */ - - gulong sig_id; /* Connexion par signal */ - -}; - -/* Panneau d'accueil par défaut (classe) */ -struct _GWelcomePanelClass -{ - GPanelItemClass parent; /* A laisser en premier */ -}; - - -/* Colonnes de la liste des messages */ -typedef enum _RecentProjectColumn -{ - RPC_VALID, /* Validité de l'entrée */ - RPC_FULLPATH, /* Chemin d'accès à un projet */ - - RPC_COUNT /* Nombre de colonnes */ - -} RecentProjectColumn; +/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ /* Initialise la classe des panneaux d'accueil par défaut. */ -static void g_welcome_panel_class_init(GWelcomePanelClass *); +static void gtk_welcome_panel_class_init(GtkWelcomePanelClass *); /* Initialise une instance de panneau d'accueil par défaut. */ -static void g_welcome_panel_init(GWelcomePanel *); +static void gtk_welcome_panel_init(GtkWelcomePanel *); /* Supprime toutes les références externes. */ -static void g_welcome_panel_dispose(GWelcomePanel *); +static void gtk_welcome_panel_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_welcome_panel_finalize(GWelcomePanel *); +static void gtk_welcome_panel_finalize(GObject *); -/* Fournit le nom interne attribué à l'élément réactif. */ -static char *g_welcome_panel_class_get_key(const GWelcomePanelClass *); +/* Réagit à un changement de sélection de la liste de panneaux. */ +static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *, GtkWelcomePanel *); -/* Fournit une indication sur la personnalité du panneau. */ -static PanelItemPersonality g_welcome_panel_class_get_personality(const GWelcomePanelClass *); +/* Réagit à une demande d'affichage de l'astuce précédente. */ +static void gtk_welcome_panel_on_prev_hint_clicked(GtkButton *, GtkWelcomePanel *); -/* Indique le chemin initial de la localisation d'un panneau. */ -static char *g_welcome_panel_class_get_path(const GWelcomePanelClass *); +/* Réagit à une demande d'affichage de l'astuce suivante. */ +static void gtk_welcome_panel_on_next_hint_clicked(GtkButton *, GtkWelcomePanel *); -/* Place un panneau dans l'ensemble affiché. */ -static void g_welcome_panel_dock(GWelcomePanel *); -/* Charge l'ensemble des astuces. */ -static void g_welcome_panel_load_tips(GWelcomePanel *); -/* Assure le dessin du fond de la bulle d'astuce. */ -static gboolean on_tip_background_draw(GtkWidget *, cairo_t *, GWelcomePanel *); +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ -/* Réagit à la demande d'étude d'un nouveau binaire. */ -static void on_new_binary_clicked(GtkButton *, GWelcomePanel *); -/* Actualise au besoin la liste des projets récents. */ -static void on_recent_list_changed(GtkRecentManager *, GWelcomePanel *); -/* Recharge une liste à jour des projets récents. */ -static void g_welcome_panel_reload_project_list(GWelcomePanel *, GtkRecentManager *); -/* Réagit à une sélection décidée d'un projet particulier. */ -static void on_row_activated_for_projects(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, GWelcomePanel *); -/* Enregistre les conditions d'affichage du panneau d'accueil. */ -static void on_startup_toggled(GtkToggleButton *, GWelcomePanel *); -/* Consulte les versions existantes et affiche une conclusion. */ -static void g_welcome_panel_check_version(GWelcomePanel *); -/* Affiche l'astuce précédente dans la liste globale. */ -static void on_tip_previous_clicked(GtkButton *, GWelcomePanel *); -/* Affiche l'astuce suivante dans la liste globale. */ -static void on_tip_next_clicked(GtkButton *, GWelcomePanel *); -/* Actualise l'affichage des astuces. */ -static void g_welcome_panel_refresh_tip(GWelcomePanel *); +/* ---------------------------------------------------------------------------------- */ +/* COEUR D'UN PANNEAU D'AFFICHAGE */ +/* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour un panneau d'accueil. */ -G_DEFINE_TYPE(GWelcomePanel, g_welcome_panel, G_TYPE_PANEL_ITEM); +G_DEFINE_TYPE(GtkWelcomePanel, gtk_welcome_panel, GTK_TYPE_TILED_PANEL); /****************************************************************************** * * -* Paramètres : klass = classe à initialiser. * +* Paramètres : class = classe à initialiser. * * * * Description : Initialise la classe des panneaux d'accueil par défaut. * * * @@ -159,28 +92,28 @@ G_DEFINE_TYPE(GWelcomePanel, g_welcome_panel, G_TYPE_PANEL_ITEM); * * ******************************************************************************/ -static void g_welcome_panel_class_init(GWelcomePanelClass *klass) +static void gtk_welcome_panel_class_init(GtkWelcomePanelClass *class) { GObjectClass *object; /* Autre version de la classe */ - GEditorItemClass *item; /* Encore une autre vision... */ - GPanelItemClass *panel; /* Version parente de classe */ + GtkWidgetClass *widget; /* Classe de haut niveau */ - object = G_OBJECT_CLASS(klass); + object = G_OBJECT_CLASS(class); - object->dispose = (GObjectFinalizeFunc/* ! */)g_welcome_panel_dispose; - object->finalize = (GObjectFinalizeFunc)g_welcome_panel_finalize; + object->dispose = gtk_welcome_panel_dispose; + object->finalize = gtk_welcome_panel_finalize; - item = G_EDITOR_ITEM_CLASS(klass); + widget = GTK_WIDGET_CLASS(class); - item->get_key = (get_item_key_fc)g_welcome_panel_class_get_key; + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/welcome.ui"); - panel = G_PANEL_ITEM_CLASS(klass); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_welcome_panel_on_selected_rows_changed)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_welcome_panel_on_prev_hint_clicked)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_welcome_panel_on_next_hint_clicked)); - panel->get_personality = (get_panel_personality_fc)g_welcome_panel_class_get_personality; - panel->dock_at_startup = gtk_panel_item_class_return_false; - panel->get_path = (get_panel_path_fc)g_welcome_panel_class_get_path; - - panel->ack_dock = (ack_undock_process_fc)g_welcome_panel_dock; + gtk_widget_class_bind_template_child(widget, GtkWelcomePanel, list); + gtk_widget_class_bind_template_child(widget, GtkWelcomePanel, properties); + gtk_widget_class_bind_template_child(widget, GtkWelcomePanel, def_child); + gtk_widget_class_bind_template_child(widget, GtkWelcomePanel, hints); } @@ -197,93 +130,81 @@ static void g_welcome_panel_class_init(GWelcomePanelClass *klass) * * ******************************************************************************/ -static void g_welcome_panel_init(GWelcomePanel *panel) +static void gtk_welcome_panel_init(GtkWelcomePanel *panel) { - GPanelItem *pitem; /* Version parente du panneau */ - GtkBuilder *builder; /* Constructeur utilisé */ - GtkTreeView *treeview; /* Affichage de la liste */ - GtkCellRenderer *renderer; /* Moteur de rendu de colonne */ - GtkTreeViewColumn *column; /* Colonne de la liste */ - GtkToggleButton *button; /* Bouton à bascule à traiter */ - bool state; /* Etat de la coche à définir */ - gchar *filename; /* Chemin d'accès à une image */ - GtkRecentManager *manager; /* Gestionnaire global */ - - /* Eléments de base */ - - pitem = G_PANEL_ITEM(panel); - - pitem->widget = G_NAMED_WIDGET(gtk_built_named_widget_new_for_panel(_("Welcome"), - _("Welcome panel"), - PANEL_WELCOME_ID)); - - panel->uorigin = !gtk_panel_item_class_dock_at_startup(G_PANEL_ITEM_GET_CLASS(pitem)); + GBytes *bytes; /* Données brutes de ressource */ + const gchar *data; /* Données brutes natives */ + int min; /* Taille à gauche minimale */ + GtkConstraintLayout *layout; /* Disposition fixant la taille*/ + GtkConstraint *constraint; /* Contrainte à considérer */ - /* Représentation graphique */ + gtk_widget_init_template(GTK_WIDGET(panel)); - builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(pitem->widget)); + panel->other_child = NULL; - /* Liste des projets récents */ + /* Chargement des astuces */ - treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); + bytes = g_resources_lookup_data("/re/chrysalide/framework/gui/panels/welcome-hints.txt", + G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); + assert(bytes != NULL); - column = gtk_tree_view_column_new(); - gtk_tree_view_append_column(treeview, column); - gtk_tree_view_set_expander_column(treeview, column); + data = g_bytes_get_data(bytes, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_add_attribute(column, renderer, "markup", RPC_FULLPATH); + panel->raw_hints = g_strsplit(data, "\n\n\n", -1); - /* Affichage au démarrage ? */ + g_bytes_unref(bytes); - button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "startup")); + panel->raw_count = g_strv_length(panel->raw_hints); + assert(panel->raw_count > 0); - g_generic_config_get_value(get_main_configuration(), MPK_WELCOME_STARTUP, &state); + panel->cur_hint = 0; - gtk_toggle_button_set_active(button, state); + /* Constitution de la liste des démarreurs */ - /* Chargement de l'image de fond */ + populate_framework_panel_launcher_list(panel->list); - filename = find_pixmap_file("tipoftheday.png"); + /* Dimensionnement de la zone d'astuces */ - panel->background = cairo_image_surface_create_from_png(filename); + gtk_widget_measure(GTK_WIDGET(panel->list), GTK_ORIENTATION_HORIZONTAL, -1, &min, NULL, NULL, NULL); - g_free(filename); + if (min > 150) + min -= 150; - /* Connexion des signaux */ + layout = GTK_CONSTRAINT_LAYOUT(gtk_widget_get_layout_manager(GTK_WIDGET(panel->hints))); - gtk_builder_add_callback_symbols(builder, - BUILDER_CALLBACK(on_tip_background_draw), - BUILDER_CALLBACK(on_new_binary_clicked), - BUILDER_CALLBACK(on_row_activated_for_projects), - BUILDER_CALLBACK(on_startup_toggled), - BUILDER_CALLBACK(on_tip_previous_clicked), - BUILDER_CALLBACK(on_tip_next_clicked), - NULL); + gtk_constraint_layout_remove_all_constraints(layout); - gtk_builder_connect_signals(builder, panel); + constraint = gtk_constraint_new_constant(NULL, + GTK_CONSTRAINT_ATTRIBUTE_LEFT, + GTK_CONSTRAINT_RELATION_EQ, + 0, + GTK_CONSTRAINT_STRENGTH_REQUIRED); + gtk_constraint_layout_add_constraint(layout, constraint); - g_object_unref(G_OBJECT(builder)); + constraint = gtk_constraint_new_constant(NULL, + GTK_CONSTRAINT_ATTRIBUTE_TOP, + GTK_CONSTRAINT_RELATION_EQ, + 0, + GTK_CONSTRAINT_STRENGTH_REQUIRED); + gtk_constraint_layout_add_constraint(layout, constraint); - /* Actualisation du contenu du panneau */ + constraint = gtk_constraint_new_constant(NULL, + GTK_CONSTRAINT_ATTRIBUTE_RIGHT, + GTK_CONSTRAINT_RELATION_EQ, + min, + GTK_CONSTRAINT_STRENGTH_REQUIRED); + gtk_constraint_layout_add_constraint(layout, constraint); - manager = get_project_manager(); + /* Premier affichage */ - panel->sig_id = g_signal_connect(manager, "changed", G_CALLBACK(on_recent_list_changed), panel); - - g_welcome_panel_reload_project_list(panel, manager); - - g_welcome_panel_load_tips(panel); - - g_welcome_panel_check_version(panel); + gtk_label_set_markup(panel->hints, panel->raw_hints[panel->cur_hint]); } /****************************************************************************** * * -* Paramètres : panel = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -293,27 +214,24 @@ static void g_welcome_panel_init(GWelcomePanel *panel) * * ******************************************************************************/ -static void g_welcome_panel_dispose(GWelcomePanel *panel) +static void gtk_welcome_panel_dispose(GObject *object) { - GtkRecentManager *manager; /* Gestionnaire global */ + GtkWelcomePanel *panel; /* Version spécialisée */ - if (panel->sig_id > 0) - { - manager = get_project_manager(); + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_WELCOME_PANEL); - g_signal_handler_disconnect(manager, panel->sig_id); - panel->sig_id = 0; + panel = GTK_WELCOME_PANEL(object); - } + g_clear_object(&panel->other_child); - G_OBJECT_CLASS(g_welcome_panel_parent_class)->dispose(G_OBJECT(panel)); + G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : panel = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -323,133 +241,25 @@ static void g_welcome_panel_dispose(GWelcomePanel *panel) * * ******************************************************************************/ -static void g_welcome_panel_finalize(GWelcomePanel *panel) +static void gtk_welcome_panel_finalize(GObject *object) { - cairo_surface_destroy(panel->background); + GtkWelcomePanel *panel; /* Version spécialisée */ - free(panel->tips); + panel = GTK_WELCOME_PANEL(object); - G_OBJECT_CLASS(g_welcome_panel_parent_class)->finalize(G_OBJECT(panel)); + g_strfreev(panel->raw_hints); -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Fournit le nom interne attribué à l'élément réactif. * -* * -* Retour : Désignation (courte) de l'élément de l'éditeur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *g_welcome_panel_class_get_key(const GWelcomePanelClass *class) -{ - char *result; /* Description à renvoyer */ - - result = strdup(PANEL_WELCOME_ID); - - return result; + G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->finalize(object); } /****************************************************************************** * * -* Paramètres : class = classe à consulter. * -* * -* Description : Fournit une indication sur la personnalité du panneau. * +* Paramètres : box = liste GTK concernée par l'appel. * +* panel = panneau d'accueil lié à la liste. * * * -* Retour : Identifiant lié à la nature unique du panneau. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PanelItemPersonality g_welcome_panel_class_get_personality(const GWelcomePanelClass *class) -{ - PanelItemPersonality result; /* Personnalité à retourner */ - - result = PIP_PERSISTENT_SINGLETON; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Indique le chemin initial de la localisation d'un panneau. * -* * -* Retour : Chemin fixé associé à la position initiale. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *g_welcome_panel_class_get_path(const GWelcomePanelClass *class) -{ - char *result; /* Emplacement à retourner */ - - result = strdup("M"); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée un panneau d'accueil par défaut. * -* * -* Retour : Adresse de la structure mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPanelItem *g_welcome_panel_new(void) -{ - GPanelItem *result; /* Structure à retourner */ - - result = g_object_new(G_TYPE_WELCOME_PANEL, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = composant à présenter à l'affichage. * -* * -* Description : Place un panneau dans l'ensemble affiché. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_welcome_panel_dock(GWelcomePanel *panel) -{ - g_welcome_panel_set_user_origin(panel, true); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = panneau d'accueil à mettre à jour. * -* * -* Description : Charge l'ensemble des astuces. * +* Description : Réagit à un changement de sélection de la liste de panneaux. * * * * Retour : - * * * @@ -457,414 +267,58 @@ static void g_welcome_panel_dock(GWelcomePanel *panel) * * ******************************************************************************/ -static void g_welcome_panel_load_tips(GWelcomePanel *panel) +static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *box, GtkWelcomePanel *panel) { - size_t i; /* Boucle de parcours */ - - char *tips[] = { - - _("There is no need to install Chrysalide on your system if you only want to give it a try.\n\n" - "Just compile the source code and run the program from there."), - - _("Chrysalide can be used in external Python scripts by setting PYTHONPATH to the directory " - "containing the 'pychrysalide.so' file. For instance:\n\n" - " cd plugins/pychrysa/.libs/\n" - " export PYTHONPATH=$PWD\n\n" - "Then run the interpreter suitable to your configuration (debug or release):\n\n" - " python3-dbg -c 'import pychrysalide ; print(pychrysalide.mod_version())'"), - - _("All the configuration files for Chrysalide are located in $HOME/.config/chrysalide/."), - - _("The behavior of the main menu bar is copied from the one of a well known browser " - "with a fox mascot.\n\n" - "To make the menu bar appear and disappear, just press and release the Alt key.") - - }; - - panel->count = ARRAY_SIZE(tips); - - panel->tips = (char **)calloc(panel->count, sizeof(char *)); - - for (i = 0; i < panel->count; i++) - panel->tips[i] = tips[i]; - - shuffle(panel->tips, panel->count, sizeof(char *)); - - panel->current = 0; - - g_welcome_panel_refresh_tip(panel); - -} - - -/****************************************************************************** -* * -* Paramètres : widget = composant graphique à redessiner. * -* cr = contexte graphique à utiliser. * -* panel = panneau associé comportant des informations utiles. * -* * -* Description : Assure le dessin du fond de la bulle d'astuce. * -* * -* Retour : FALSE pour poursuivre la propagation de l'événement. * -* * -* Remarques : - * -* * -******************************************************************************/ + GtkListBoxRow *row; /* Ligne sélectionnée */ + GtkWidget *new; /* Nouvelles propriétés */ -static gboolean on_tip_background_draw(GtkWidget *widget, cairo_t *cr, GWelcomePanel *panel) -{ - int wgt_width; /* Largeur disponible totale */ - int wgt_height; /* Hauteur disponible totale */ - int img_width; /* Largeur de l'image de fond */ - int img_height; /* Hauteur de l'image de fond */ - double scale; /* Echelle à appliquer */ + row = gtk_list_box_get_selected_row(box); - if (cairo_surface_status(panel->background) == CAIRO_STATUS_SUCCESS) + /** + * Perte de sélection : bascule sur les informations d'accueil. + */ + if (row == NULL) { - wgt_width = gtk_widget_get_allocated_width(widget); - wgt_height = gtk_widget_get_allocated_height(widget); - - img_width = cairo_image_surface_get_width(panel->background); - img_height = cairo_image_surface_get_height(panel->background); + assert(panel->other_child != NULL); - scale = wgt_height / (2.0 * img_height); + gtk_stack_set_visible_child(panel->properties, panel->def_child); - cairo_scale(cr, scale, scale); - - cairo_set_source_surface(cr, panel->background, - (wgt_width / scale) - img_width, - ((wgt_height / scale) - img_height) / 2); - - cairo_paint(cr); + gtk_stack_remove(panel->properties, panel->other_child); + panel->other_child = NULL; } - return FALSE; - -} - - -/****************************************************************************** -* * -* Paramètres : button = bouton impliqué dans la procédure. * -* panel = panneau associé comportant des informations utiles. * -* * -* Description : Réagit à la demande d'étude d'un nouveau binaire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_new_binary_clicked(GtkButton *button, GWelcomePanel *panel) -{ - GObject *ref; /* Espace de référencements */ - GtkMenuItem *item; /* Elément de menu simulé */ - - ref = G_OBJECT(get_editor_window()); - - item = GTK_MENU_ITEM(g_object_get_data(ref, "mnu_project_add_binary")); - - g_object_unref(ref); - - gtk_menu_item_activate(item); - -} - - -/****************************************************************************** -* * -* Paramètres : manager = gestion de fichiers récemment utilisés. * -* panel = panneau associé comportant des informations utiles.* -* * -* Description : Actualise au besoin la liste des projets récents. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_recent_list_changed(GtkRecentManager *manager, GWelcomePanel *panel) -{ - g_welcome_panel_reload_project_list(panel, manager); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = panneau comportant des informations utiles. * -* manager = gestion de fichiers récemment utilisés. * -* * -* Description : Recharge une liste à jour des projets récents. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_welcome_panel_reload_project_list(GWelcomePanel *panel, GtkRecentManager *manager) -{ - GtkBuilder *builder; /* Constructeur utilisé */ - GtkListStore *store; /* Modèle de gestion */ - bool empty; /* Liste vide ? */ - GList *recents; /* Liste des fichiers récents */ - GList *recent; /* Elément à traiter */ - GtkRecentInfo *info; /* Informations sur l'élément */ - GtkTreeIter iter; /* Point d'insertion */ - - /* Réinitialisation */ - - builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); - - store = GTK_LIST_STORE(gtk_builder_get_object(builder, "store")); - - gtk_list_store_clear(store); - - empty = true; - - /* Chargement */ - - recents = gtk_recent_manager_get_items(manager); - - if (recents != NULL) - { - for (recent = g_list_first(recents); recent != NULL; recent = g_list_next(recent)) - { - info = recent->data; - - if (strcmp(gtk_recent_info_get_mime_type(info), "application/chrysalide.project") == 0) - { - gtk_list_store_append(store, &iter); - - gtk_list_store_set(store, &iter, - RPC_VALID, true, - RPC_FULLPATH, gtk_recent_info_get_uri_display(info), - -1); - - empty = false; - - } - - gtk_recent_info_unref(info); - - } - - g_list_free(recents); - - } - - /* Indication par défaut */ - if (empty) - { - gtk_list_store_append(store, &iter); - - gtk_list_store_set(store, &iter, - RPC_VALID, false, - RPC_FULLPATH, _("<i>(No recent project)</i>"), - -1); - - } - - g_object_unref(G_OBJECT(builder)); - -} - - -/****************************************************************************** -* * -* Paramètres : treeview = liste graphique concernée par la procédure. * -* path = chemin d'accès à la ligne sélectionnée. * -* column = colonne concernée par la sélection. * -* panel = panneau associé avec des informations utiles. * -* * -* Description : Réagit à une sélection décidée d'un projet particulier. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_row_activated_for_projects(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, GWelcomePanel *panel) -{ - GtkTreeModel *model; /* Modèle de gestion */ - GtkTreeIter iter; /* Point de la consultation */ - gboolean valid; /* Validité de l'entrée */ - gchar *filename; /* Chemin d'accès au projet */ - GStudyProject *project; /* Nouveau projet à ouvrir */ - - model = gtk_tree_view_get_model(treeview); - - if (gtk_tree_model_get_iter(model, &iter, path)) + /** + * Bascule vers une nouvelle fenêtre. + */ + else { - gtk_tree_model_get(model, &iter, RPC_VALID, &valid, RPC_FULLPATH, &filename, -1); + new = get_framework_panel_parameters(row); + assert(new != NULL); - if (valid) + if (new != panel->other_child) { - project = g_study_project_open(filename, true); - - if (project != NULL) - { - set_current_project(project); - - push_project_into_recent_list(project); - - } - - g_free(filename); - - } - - } - -} - - -/****************************************************************************** -* * -* Paramètres : button = bouton de défilement des astuces activé; * -* panel = panneau associé comportant des informations utiles. * -* * -* Description : Enregistre les conditions d'affichage du panneau d'accueil. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void on_startup_toggled(GtkToggleButton *button, GWelcomePanel *panel) -{ - g_generic_config_set_value(get_main_configuration(), - MPK_WELCOME_STARTUP, gtk_toggle_button_get_active(button)); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = panneau d'accueil à mettre à jour. * -* * -* Description : Consulte les versions existantes et affiche une conclusion. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_welcome_panel_check_version(GWelcomePanel *panel) -{ - bool skip; /* Saut de la vérification */ - bool unknown; /* Impossibilité de comparaison*/ - int current; /* Version courante */ - int sock; /* Canal de communication */ - bool status; /* Bilan d'une communication */ - char buffer[1024]; /* Tampon de réception */ - size_t got; /* Quantité de données reçues */ - char *version; /* Version récupérée */ - int available; /* Version disponible */ - GtkBuilder *builder; /* Constructeur utilisé */ - GtkLabel *label; /* Etiquette à éditer */ - char *msg; /* Message à faire paraître */ - - g_generic_config_get_value(get_main_configuration(), MPK_WELCOME_CHECK, &skip); - skip = !skip; - - unknown = true; - - current = atoi(VERSION); - - if (skip) goto check_process; - - /* Recherche en ligne */ + gtk_stack_add_child(panel->properties, new); + gtk_stack_set_visible_child(panel->properties, new); - sock = connect_via_tcp("www.chrysalide.re", "80", NULL); - if (sock == -1) goto check_process; + if (panel->other_child != NULL) + gtk_stack_remove(panel->properties, panel->other_child); -#define REQUEST "GET /version.last HTTP/1.1\r\nHost: www.chrysalide.re\r\n\r\n" - - status = safe_send(sock, REQUEST, strlen(REQUEST), 0); - if (!status) goto check_done; - - status = recv_all(sock, buffer, sizeof(buffer), &got); - if (!status) goto check_done; - - version = strstr(buffer, "\r\n\r\n"); - - if (version != NULL) - { - available = atoi(version + 4); - - unknown = false; - - } - - check_done: - - close(sock); - - check_process: - - /* Affichage */ - - builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); - - label = GTK_LABEL(gtk_builder_get_object(builder, "version")); - - if (skip) - asprintf(&msg, - "Your version is: <b>%d</b>\n\n" \ - "Automatic version check is disabled.", - current); - - else - { - if (unknown) - asprintf(&msg, - "Your version is: <b>%d</b>\n\n" \ - "Lastest available version is unknown.", - current); - - else - { - if (current >= available) - asprintf(&msg, - "Your version is: <b>%d</b>\n\n" \ - "Lastest version is: <b>%d</b>\n\n" \ - "Your software is <span color='green'><b>up-to-date</b></span>.", - current, available); - - else - asprintf(&msg, - "Your version is: <b>%d</b>\n\n" \ - "Lastest version is: <b>%d</b>\n\n" \ - "Your software is <span color='red'><b>outdated</b></span>.", - current, available); + panel->other_child = new; } } - gtk_label_set_markup(label, msg); - - free(msg); - - g_object_unref(G_OBJECT(builder)); - } /****************************************************************************** * * -* Paramètres : button = bouton de défilement des astuces activé; * -* panel = panneau associé comportant des informations utiles. * +* Paramètres : button = bouton GTK concerné par l'appel. * +* panel = panneau d'accueil lié à la liste. * * * -* Description : Affiche l'astuce précédente dans la liste globale. * +* Description : Réagit à une demande d'affichage de l'astuce précédente. * * * * Retour : - * * * @@ -872,24 +326,24 @@ static void g_welcome_panel_check_version(GWelcomePanel *panel) * * ******************************************************************************/ -static void on_tip_previous_clicked(GtkButton *button, GWelcomePanel *panel) +static void gtk_welcome_panel_on_prev_hint_clicked(GtkButton *button, GtkWelcomePanel *panel) { - if (panel->current > 0) - panel->current--; + if (panel->cur_hint > 0) + panel->cur_hint--; else - panel->current = panel->count - 1; + panel->cur_hint = panel->raw_count - 1; - g_welcome_panel_refresh_tip(panel); + gtk_label_set_markup(panel->hints, panel->raw_hints[panel->cur_hint]); } /****************************************************************************** * * -* Paramètres : button = bouton de défilement des astuces activé; * -* panel = panneau associé comportant des informations utiles. * +* Paramètres : button = bouton GTK concerné par l'appel. * +* panel = panneau d'accueil lié à la liste. * * * -* Description : Affiche l'astuce suivante dans la liste globale. * +* Description : Réagit à une demande d'affichage de l'astuce suivante. * * * * Retour : - * * * @@ -897,82 +351,27 @@ static void on_tip_previous_clicked(GtkButton *button, GWelcomePanel *panel) * * ******************************************************************************/ -static void on_tip_next_clicked(GtkButton *button, GWelcomePanel *panel) +static void gtk_welcome_panel_on_next_hint_clicked(GtkButton *button, GtkWelcomePanel *panel) { - if ((panel->current + 1) < panel->count) - panel->current++; + if ((panel->cur_hint + 1) < panel->raw_count) + panel->cur_hint++; else - panel->current = 0; + panel->cur_hint = 0; - g_welcome_panel_refresh_tip(panel); + gtk_label_set_markup(panel->hints, panel->raw_hints[panel->cur_hint]); } -/****************************************************************************** -* * -* Paramètres : panel = panneau associé comportant des informations utiles. * -* * -* Description : Actualise l'affichage des astuces. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ -static void g_welcome_panel_refresh_tip(GWelcomePanel *panel) -{ - GtkBuilder *builder; /* Constructeur utilisé */ - GtkLabel *label; /* Etiquette de présentation */ +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ - assert(panel->current < panel->count); - builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); - label = GTK_LABEL(gtk_builder_get_object(builder, "tip")); - gtk_label_set_markup(label, panel->tips[panel->current]); - g_object_unref(G_OBJECT(builder)); -} -/****************************************************************************** -* * -* Paramètres : panel = panneau associé comportant des informations utiles. * -* * -* Description : Indique l'origine de l'affichage du panneau d'accueil. * -* * -* Retour : true si l'affichage est le fait de l'utilisateur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_welcome_panel_get_user_origin(const GWelcomePanel *panel) -{ - return panel->uorigin; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = panneau associé comportant des informations utiles.* -* uorigin = true si l'affichage est le fait de l'utilisateur. * -* * -* Description : Détermine l'origine de l'affichage du panneau d'accueil. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_welcome_panel_set_user_origin(GWelcomePanel *panel, bool uorigin) -{ - panel->uorigin = uorigin; - -} diff --git a/src/gui/panels/welcome.h b/src/gui/panels/welcome.h index 5cdd6a1..d9ea18d 100644 --- a/src/gui/panels/welcome.h +++ b/src/gui/panels/welcome.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * welcome.h - prototypes pour le panneau d'accueil par défaut * - * Copyright (C) 2012-2019 Cyrille Bagard + * Copyright (C) 2012-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,42 +26,17 @@ #define _GUI_PANELS_WELCOME_H -#include <i18n.h> +#include <gtk/gtk.h> -#include "../panel.h" +#include "../../glibext/helpers.h" +#include "../../gtkext/panel.h" -#define PANEL_WELCOME_ID "welcome" +#define GTK_TYPE_WELCOME_PANEL (gtk_welcome_panel_get_type()) - -#define G_TYPE_WELCOME_PANEL g_welcome_panel_get_type() -#define G_WELCOME_PANEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_welcome_panel_get_type(), GWelcomePanel)) -#define G_IS_WELCOME_PANEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_welcome_panel_get_type())) -#define G_WELCOME_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WELCOME_PANEL, GWelcomePanelClass)) -#define G_IS_WELCOME_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WELCOME_PANEL)) -#define G_WELCOME_PANEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WELCOME_PANEL, GWelcomePanelClass)) - - -/* Panneau d'accueil par défaut (instance) */ -typedef struct _GWelcomePanel GWelcomePanel; - -/* Panneau d'accueil par défaut (classe) */ -typedef struct _GWelcomePanelClass GWelcomePanelClass; - - -/* Indique le type défini pour un panneau d'accueil. */ -GType g_welcome_panel_get_type(void); - -/* Crée un panneau d'accueil par défaut. */ -GPanelItem *g_welcome_panel_new(void); - -/* Indique l'origine de l'affichage du panneau d'accueil. */ -bool g_welcome_panel_get_user_origin(const GWelcomePanel *); - -/* Détermine l'origine de l'affichage du panneau d'accueil. */ -void g_welcome_panel_set_user_origin(GWelcomePanel *, bool); +DECLARE_GTYPE(GtkWelcomePanel, gtk_welcome_panel, GTK, WELCOME_PANEL); diff --git a/src/gui/panels/welcome.ui b/src/gui/panels/welcome.ui index d016907..c545b1d 100644 --- a/src/gui/panels/welcome.ui +++ b/src/gui/panels/welcome.ui @@ -1,245 +1,168 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.18.3 --> <interface> - <requires lib="gtk+" version="3.12"/> - <object class="GtkImage" id="image1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-new</property> - <property name="icon_size">6</property> - </object> - <object class="GtkListStore" id="store"> - <columns> - <!-- column-name valid --> - <column type="gboolean"/> - <!-- column-name fullpath --> - <column type="gchararray"/> - </columns> - </object> - <object class="GtkOffscreenWindow" id="offscreenwindow1"> - <property name="can_focus">False</property> - <child> - <object class="GtkBox" id="box"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="homogeneous">True</property> - <child> - <object class="GtkBox" id="box2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="margin_left">8</property> - <property name="margin_right">8</property> - <property name="margin_top">8</property> - <property name="margin_bottom">8</property> - <property name="orientation">vertical</property> - <property name="spacing">8</property> - <child> - <object class="GtkAlignment" id="alignment1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">center</property> - <property name="top_padding">180</property> - <property name="bottom_padding">30</property> - <child> - <object class="GtkButton" id="button1"> - <property name="label" translatable="yes">New binary...</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="image">image1</property> - <property name="image_position">top</property> - <property name="always_show_image">True</property> - <signal name="clicked" handler="on_new_binary_clicked" swapped="no"/> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Last projects:</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkTreeView" id="treeview"> - <property name="height_request">250</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="margin_left">8</property> - <property name="hexpand">True</property> - <property name="model">store</property> - <property name="headers_visible">False</property> - <property name="rules_hint">True</property> - <signal name="row-activated" handler="on_row_activated_for_projects" swapped="no"/> - <child internal-child="selection"> - <object class="GtkTreeSelection" id="treeview-selection1"/> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="startup"> - <property name="label" translatable="yes">Show this panel at startup.</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="halign">start</property> - <property name="valign">end</property> - <property name="vexpand">True</property> - <property name="xalign">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_startup_toggled" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> + + <template class="GtkWelcomePanel" parent="GtkTiledPanel"> <child> - <object class="GtkBox" id="box3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="margin_left">8</property> - <property name="margin_right">8</property> - <property name="margin_top">8</property> - <property name="margin_bottom">8</property> - <property name="orientation">vertical</property> - <property name="spacing">8</property> - <child> - <object class="GtkBox" id="box4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="homogeneous">True</property> + <object class="GtkScrolledWindow"> + <property name="hscrollbar-policy">automatic</property> + <property name="vscrollbar-policy">automatic</property> + <property name="hexpand">TRUE</property> + <property name="vexpand">TRUE</property> + <property name="has-frame">0</property> <child> - <object class="GtkLabel" id="version"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="opacity">0.81999999977648264</property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="opacity">0.81999999999999995</property> - <property name="label" translatable="yes">Get access to the online documentation and stay tuned by visiting the official website : <a href="http://chrysalide.re">chrysalide.re</a> - -You can also follow Chrysalide on Twitter : <a href="http://twitter.com/chrysalide_ref">@chrysalide_ref</a></property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - <property name="track_visited_links">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="tip"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">1</property> - <property name="xpad">8</property> - <property name="ypad">8</property> - <property name="label" translatable="yes">label</property> - <property name="wrap">True</property> - <property name="selectable">True</property> - <signal name="draw" handler="on_tip_background_draw" swapped="no"/> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButtonBox" id="buttonbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">8</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="button3"> - <property name="label" translatable="yes">Previous</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <signal name="clicked" handler="on_tip_previous_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="button2"> - <property name="label" translatable="yes">Next</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <signal name="clicked" handler="on_tip_next_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> + <object class="GtkGrid"> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="column-spacing">64</property> + <property name="column-homogeneous">TRUE</property> + <property name="margin-bottom">32</property> + <property name="margin-end">32</property> + <property name="margin-start">32</property> + <property name="margin-top">32</property> + + <!-- Launcher list --> + <child> + <object class="GtkListBox" id="list"> + <property name="halign">end</property> + <property name="valign">center</property> + <property name="selection-mode">GTK_SELECTION_SINGLE</property> + <property name="activate-on-single-click">0</property> + + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + + <style> + <class name="boxed-list"/> + <class name="frame"/> + </style> + + <signal name="selected-rows-changed" handler="gtk_welcome_panel_on_selected_rows_changed"/> + + </object> + </child> + + <!-- Hints / options --> + <child> + <object class="GtkStack" id="properties"> + <property name="halign">start</property> + <property name="valign">fill</property> + + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> + + <child> + + <object class="GtkStackPage"> + <property name="name">default</property> + <property name="title">Welcome</property> + + <property name="child"> + <object class="GtkGrid" id="def_child"> + <property name="halign">start</property> + <property name="valign">fill</property> + <property name="column-spacing">16</property> + <property name="column-homogeneous">FALSE</property> + + <!-- Text --> + + <child> + <object class="GtkLabel" id="hints"> + <property name="halign">start</property> + <property name="valign">fill</property> + <property name="xalign">0</property> + <property name="yalign">1</property> + <property name="vexpand">1</property> + <property name="width-request">50</property> + <property name="wrap">TRUE</property> + + <property name="layout-manager"> + <object class="GtkConstraintLayout"> + <!-- + <constraints> + + <constraint target="super" target-attribute="left" + relation="eq" + constant="0" + strength="required"/> + + <constraint target="super" target-attribute="top" + relation="eq" + constant="0" + strength="required"/> + + <constraint target="super" target-attribute="right" + relation="eq" + constant="340" + strength="required"/> + + </constraints> + --> + </object> + </property> + + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + + </object> + </child> + + <!-- Prev/Next buttons --> + <child> + <object class="GtkBox"> + <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property> + <property name="halign">fill</property> + <property name="valign">center</property> + <property name="homogeneous">TRUE</property> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + + <child> + <object class="GtkButton"> + <property name="icon-name">go-previous-symbolic</property> + <property name="halign">end</property> + <signal name="clicked" handler="gtk_welcome_panel_on_prev_hint_clicked"/> + </object> + </child> + + <child> + <object class="GtkButton"> + <property name="icon-name">go-next-symbolic</property> + <property name="halign">start</property> + <signal name="clicked" handler="gtk_welcome_panel_on_next_hint_clicked"/> + </object> + </child> + + <style> + <class name="linked"/> + </style> + + </object> + </child> + + <style> + <class name="hints"/> + </style> + + </object> + + </property> + </object> + </child> + + </object> + </child> + </object> </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> + </object> </child> - </object> - </child> - </object> + </template> + </interface> diff --git a/src/gui/style.css b/src/gui/style.css new file mode 100644 index 0000000..dce41fa --- /dev/null +++ b/src/gui/style.css @@ -0,0 +1,71 @@ + +/* Extension de style */ + +.dim-label { + + margin-bottom: 4px; + +} + + +.boxed-widget { + + border-top-left-radius: 12px; + border-top-right-radius: 12px; + + border-bottom-left-radius: 12px; + border-bottom-right-radius: 12px; + +} + + +list.boxed-list, list.boxed-list > row:first-child { + + border-top-left-radius: 12px; + border-top-right-radius: 12px; + +} + +list.boxed-list, list.boxed-list > row:last-child { + + border-bottom-left-radius: 12px; + border-bottom-right-radius: 12px; + +} + + +/* about.css */ + +@import url('resource:///re/chrysalide/framework/gui/dialogs/about.css'); + + +/* welcome.css */ + +grid.hints { + + background-image: -gtk-icontheme('tipoftheday-symbolic'); + background-position: right top; + background-size: 200px auto; + background-repeat: no-repeat; + + min-height: 250px; + +} + +grid.hints > box { + + min-width: 130px; + +} + + +/* dockstation.css */ + +.control-button { + + min-height: 0; + min-width: 0; + + padding: 6px; + +} diff --git a/src/gui/window-int.h b/src/gui/window-int.h new file mode 100644 index 0000000..4f3dd57 --- /dev/null +++ b/src/gui/window-int.h @@ -0,0 +1,60 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * window.h - prototypes internes pour la construction d'une fenêtre graphique principale + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_WINDOW_INT_H +#define _GUI_WINDOW_INT_H + + +#include "window.h" +#include "../gtkext/grid.h" + + + +/* Définition de l'application principale graphique (instance) */ +struct _GtkFrameworkWindow +{ + GtkApplicationWindow parent; /* A laisser en premier */ + + GSettings *settings; /* Paramètres globaux */ + + GtkTilingGrid *grid; /* Réceptacle de panneaux */ + GtkStatusStack *status; /* Barre de statut */ + + GtkTiledPanel *main; /* Panneau principal courant */ + +}; + +/* Définition de l'application principale graphique (classe) */ +struct _GtkFrameworkWindowClass +{ + GtkApplicationWindowClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une fenêtre principale pour Chrysalide. */ +bool gtk_framework_window_create(GtkFrameworkWindow *, GtkApplication *); + + + +#endif /* _GUI_WINDOW_INT_H */ diff --git a/src/gui/window.c b/src/gui/window.c new file mode 100644 index 0000000..e14ecf7 --- /dev/null +++ b/src/gui/window.c @@ -0,0 +1,621 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * window.c - construction d'une fenêtre graphique principale + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "window.h" + + +#include "window-int.h" +#include "core/panels.h" +#include "dialogs/about.h" +#include "dialogs/preferences.h" +#include "panels/logs.h" +#include "panels/welcome.h" +#include "../gtkext/grid.h" +#include "../gtkext/helpers.h" + + + +/* Initialise la classe des applications majeures de Chrysalide. */ +static void gtk_framework_window_class_init(GtkFrameworkWindowClass *); + +/* Initialise une application principale pour Chrysalide. */ +static void gtk_framework_window_init(GtkFrameworkWindow *); + +/* Supprime toutes les références externes. */ +static void gtk_framework_window_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_framework_window_finalize(GObject *); + +/* Bascule l'affichage d'un panneau de bordure. */ +static void gtk_framework_window_toggle_pannel_visibility(GtkFrameworkWindow *, TilingGridBorder); + +/* Réagit à une activation de bascule du panneau supérieur. */ +static void gtk_framework_window_activate_toggle_top(GSimpleAction *, GVariant *, gpointer); + +/* Réagit à une activation de bascule du panneau de gauche. */ +static void gtk_framework_window_activate_toggle_left(GSimpleAction *action, GVariant *, gpointer); + +/* Réagit à une activation de bascule du panneau de droite. */ +static void gtk_framework_window_activate_toggle_right(GSimpleAction *, GVariant *, gpointer); + +/* Réagit à une activation de bascule du panneau inférieur. */ +static void gtk_framework_window_activate_toggle_bottom(GSimpleAction *, GVariant *, gpointer); + +/* Réagit à une activation du menu "Préférences" de la fenetre. */ +static void gtk_framework_window_activate_preferences(GSimpleAction *, GVariant *, gpointer); + +/* Réagit à une activation du menu "A propos de" de la fenetre. */ +static void gtk_framework_window_activate_about(GSimpleAction *, GVariant *, gpointer); + + + +/* Indique le type défini pour une fenêtre graphique principale de Chrysalide. */ +G_DEFINE_TYPE(GtkFrameworkWindow, gtk_framework_window, GTK_TYPE_APPLICATION_WINDOW); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des applications majeures de Chrysalide.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_class_init(GtkFrameworkWindowClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)gtk_framework_window_dispose; + object->finalize = (GObjectFinalizeFunc)gtk_framework_window_finalize; + + widget = GTK_WIDGET_CLASS(class); + + g_type_ensure(GTK_TYPE_TILING_GRID); + g_type_ensure(GTK_TYPE_STATUS_STACK); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/window.ui"); + + gtk_widget_class_bind_template_child(widget, GtkFrameworkWindow, grid); + gtk_widget_class_bind_template_child(widget, GtkFrameworkWindow, status); + + /* Active une action native (cf. https://docs.gtk.org/gtk4/class.Window.html#actions) */ + gtk_widget_class_add_binding_action(widget, GDK_KEY_Q, GDK_CONTROL_MASK, "window.close", NULL); + + gtk_widget_class_add_binding_action(widget, GDK_KEY_comma, GDK_CONTROL_MASK, "win.preferences", NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : window = instance à initialiser. * +* * +* Description : Initialise une application principale pour Chrysalide. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_init(GtkFrameworkWindow *window) +{ + GAction *action; /* Action mise en place */ + + static GActionEntry app_entries[] = { + { "toggle-top", gtk_framework_window_activate_toggle_top, NULL, NULL, NULL }, + { "toggle-left", gtk_framework_window_activate_toggle_left, NULL, NULL, NULL }, + { "toggle-right", gtk_framework_window_activate_toggle_right, NULL, NULL, NULL }, + { "toggle-bottom", gtk_framework_window_activate_toggle_bottom, NULL, NULL, NULL }, + { "preferences", gtk_framework_window_activate_preferences, NULL, NULL, NULL }, + { "about", gtk_framework_window_activate_about, NULL, NULL, NULL }, + }; + + gtk_widget_init_template(GTK_WIDGET(window)); + + window->settings = g_settings_new(FRAMEWORK_WINDOW_ID); + + g_settings_bind(window->settings, "window-width", G_OBJECT(window), "default-width", G_SETTINGS_BIND_DEFAULT); + g_settings_bind(window->settings, "window-height", G_OBJECT(window), "default-height", G_SETTINGS_BIND_DEFAULT); + g_settings_bind(window->settings, "window-maximized", G_OBJECT(window), "maximized", G_SETTINGS_BIND_DEFAULT); + + window->main = NULL; + + g_action_map_add_action_entries(G_ACTION_MAP(window), + app_entries, G_N_ELEMENTS(app_entries), + window); + + /** + * Définition de l'accès aux actions pour obtenir un effet de bord sur + * l'accès aux boutons graphiques de déclenchement. + */ + + action = g_action_map_lookup_action(G_ACTION_MAP(window), "toggle-top"); + + g_object_bind_property(G_OBJECT(window->grid), "empty-top", + G_OBJECT(action), "enabled", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + action = g_action_map_lookup_action(G_ACTION_MAP(window), "toggle-left"); + + g_object_bind_property(G_OBJECT(window->grid), "empty-left", + G_OBJECT(action), "enabled", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + action = g_action_map_lookup_action(G_ACTION_MAP(window), "toggle-right"); + + g_object_bind_property(G_OBJECT(window->grid), "empty-right", + G_OBJECT(action), "enabled", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + action = g_action_map_lookup_action(G_ACTION_MAP(window), "toggle-bottom"); + + g_object_bind_property(G_OBJECT(window->grid), "empty-bottom", + G_OBJECT(action), "enabled", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_dispose(GObject *object) +{ + GtkFrameworkWindow *window; /* Version spécialisée */ + + window = GTK_FRAMEWORK_WINDOW(object); + + gtk_widget_dispose_template(GTK_WIDGET(window), GTK_TYPE_FRAMEWORK_WINDOW); + + g_clear_object(&window->settings); + + g_clear_object(&window->main); + + G_OBJECT_CLASS(gtk_framework_window_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_framework_window_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : app = application GTK de rattachement. * +* * +* Description : Crée une nouvelle application principale pour Chrysalide. * +* * +* Retour : Mécanismes mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkApplicationWindow *gtk_framework_window_new(GtkApplication *app) +{ + GtkApplicationWindow *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_FRAMEWORK_WINDOW, NULL); + + if (!gtk_framework_window_create(GTK_FRAMEWORK_WINDOW(result), app)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : window = instance de fenêtre principale à remplir. * +* app = application GTK de rattachement. * +* * +* Description : Met en place une fenêtre principale pour Chrysalide. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_framework_window_create(GtkFrameworkWindow *window, GtkApplication *app) +{ + bool result; /* Bilan à retourner */ + GtkTiledPanel *panel; /* Panneau d'affichage */ + GtkCssProvider *css; /* Feuille de style maison */ + + result = true; + + gtk_window_set_application(GTK_WINDOW(window), app); + + /* Inclusion d'un écran d'accueil */ + + panel = get_framework_panel_singleton(GTK_TYPE_WELCOME_PANEL); + + gtk_framework_window_add(window, panel); + + if (1/* FIXME : first time */) + { + panel = get_framework_panel_singleton(GTK_TYPE_LOGS_PANEL); + + gtk_framework_window_add(window, panel); + + } + + /* Chargement des extensions de thème */ + + css = gtk_css_provider_new(); + + gtk_css_provider_load_from_resource(css, "/re/chrysalide/framework/gtkext/hexview.css"); + + gtk_style_context_add_provider_for_display(gtk_widget_get_display(GTK_WIDGET(window)), + GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + unref_object(css); + + css = gtk_css_provider_new(); + + gtk_css_provider_load_from_resource(css, "/re/chrysalide/framework/gui/style.css"); + + gtk_style_context_add_provider_for_display(gtk_widget_get_display(GTK_WIDGET(window)), + GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + unref_object(css); + + /* Fin des chargements */ + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : window = instance de fenêtre principale à manipuler. * +* border = sélection de la zone à considérer. * +* * +* Description : Bascule l'affichage d'un panneau de bordure. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_toggle_pannel_visibility(GtkFrameworkWindow *window, TilingGridBorder border) +{ + bool state; /* Etat courant à basculer */ + + state = gtk_tiling_grid_get_visible(window->grid, border); + + state = !state; + + gtk_tiling_grid_set_visible(window->grid, border, state); + +} + + +/****************************************************************************** +* * +* Paramètres : action = désignation de l'action concernée par l'appel. * +* unused = adresse non utilisée ici. * +* _window = instance de fenêtre principale à manipuler. * +* * +* Description : Réagit à une activation de bascule du panneau supérieur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_activate_toggle_top(GSimpleAction *action, GVariant *unused, gpointer _window) +{ + gtk_framework_window_toggle_pannel_visibility(_window, TGB_TOP); + +} + + +/****************************************************************************** +* * +* Paramètres : action = désignation de l'action concernée par l'appel. * +* unused = adresse non utilisée ici. * +* _window = instance de fenêtre principale à manipuler. * +* * +* Description : Réagit à une activation de bascule du panneau de gauche. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_activate_toggle_left(GSimpleAction *action, GVariant *unused, gpointer _window) +{ + gtk_framework_window_toggle_pannel_visibility(_window, TGB_LEFT); + +} + + +/****************************************************************************** +* * +* Paramètres : action = désignation de l'action concernée par l'appel. * +* unused = adresse non utilisée ici. * +* _window = instance de fenêtre principale à manipuler. * +* * +* Description : Réagit à une activation de bascule du panneau de droite. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_activate_toggle_right(GSimpleAction *action, GVariant *unused, gpointer _window) +{ + gtk_framework_window_toggle_pannel_visibility(_window, TGB_RIGHT); + +} + + +/****************************************************************************** +* * +* Paramètres : action = désignation de l'action concernée par l'appel. * +* unused = adresse non utilisée ici. * +* _window = instance de fenêtre principale à manipuler. * +* * +* Description : Réagit à une activation de bascule du panneau inférieur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_activate_toggle_bottom(GSimpleAction *action, GVariant *unused, gpointer _window) +{ + gtk_framework_window_toggle_pannel_visibility(_window, TGB_BOTTOM); + +} + + +/****************************************************************************** +* * +* Paramètres : action = désignation de l'action concernée par l'appel. * +* unused = adresse non utilisée ici. * +* _window = instance de fenêtre principale à manipuler. * +* * +* Description : Réagit à une activation du menu "Préférences" de la fenetre. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_activate_preferences(GSimpleAction *action, GVariant *unused, gpointer _window) +{ + GtkFrameworkWindow *window; /* Fenêtre principale associée */ + GtkWindow *dialog; /* Boîte de dialogue à afficher*/ + + window = _window; + + dialog = gtk_preferences_dialog_new(GTK_WINDOW(window)); + + gtk_window_present(dialog); + +} + + +/****************************************************************************** +* * +* Paramètres : action = désignation de l'action concernée par l'appel. * +* unused = adresse non utilisée ici. * +* _window = instance de fenêtre principale à manipuler. * +* * +* Description : Réagit à une activation du menu "A propos de" de la fenetre. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_activate_about(GSimpleAction *action, GVariant *unused, gpointer _window) +{ + GtkFrameworkWindow *window; /* Fenêtre principale associée */ + GtkWindow *dialog; /* Boîte de dialogue à afficher*/ + + window = _window; + + dialog = gtk_app_about_dialog_new(GTK_WINDOW(window)); + + gtk_window_present(dialog); + +} + + +/****************************************************************************** +* * +* Paramètres : window = instance de fenêtre principale à consulter. * +* * +* Description : Fournit une référence à la barre de statut intégrée. * +* * +* Retour : Composant GTK en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkStatusStack *gtk_framework_window_get_status_stack(const GtkFrameworkWindow *window) +{ + GtkStatusStack *result; /* Instance à retourner */ + + result = window->status; + ref_object(result); + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : window = instance de fenêtre principale à remplir. * +* panel = nouveau panneau à afficher. * +* * +* Description : Ajoute un panneau à la fenêtre principale de Chrysalide. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_framework_window_add(GtkFrameworkWindow *window, /* __steal */GtkTiledPanel *panel) +{ + GtkWidget *bar; /* Barre de titre */ + GListStore *list; /* Liste éventuelle à intégrer */ + guint count; /* Nombre d'élements présents */ + guint i; /* Boucle de parcours */ + GtkWidget *widget; /* Composant à intégrer */ + FrameworkPanelPersonality personality; /* Propriétés du panneau */ + + + + gtk_tiling_grid_add_panel(window->grid, panel, G_OBJECT_TYPE(panel) == GTK_TYPE_WELCOME_PANEL); + + + + + bar = gtk_window_get_titlebar(GTK_WINDOW(window)); + + if (bar == NULL) ////////// REMME + { + bar = gtk_header_bar_new(); + gtk_window_set_titlebar(GTK_WINDOW(window), bar); + } ///////////////////////////// + + list = gtk_tiled_panel_get_title_widgets(panel, false); + + if (list != NULL) + { + count = g_list_model_get_n_items(G_LIST_MODEL(list)); + + for (i = 0; i < count; i++) + { + widget = GTK_WIDGET(g_list_model_get_item(G_LIST_MODEL(list), i)); + gtk_header_bar_pack_end(GTK_HEADER_BAR(bar), widget); + } + + unref_object(list); + + } + + /* Mise à jour des liens vers un panneau principal */ + + personality = get_framework_panel_personality(G_OBJECT_TYPE(panel)); + + if (personality & FPP_MAIN_PANEL) + gtk_framework_window_notify_new_main_panel_state(window, panel, true); + + else + { + if (window->main != NULL) + gtk_tiled_panel_notify_new_main_panel_state(panel, window->main, true); + } + +} + + +/****************************************************************************** +* * +* Paramètres : window = instance de fenêtre principale à manipuler. * +* main = panneau principal visé par l'opération. * +* activated = nature du changement de statut : ajout, retrait ?* +* * +* Description : Note un ajout ou un retrait de panneau principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_framework_window_notify_new_main_panel_state(GtkFrameworkWindow *window, GtkTiledPanel *main, bool activated) +{ + if (activated) + { + g_clear_object(&window->main); + + window->main = main; + ref_object(main); + + } + + else + { + if (main == window->main) + g_clear_object(&window->main); + + } + + gtk_tiling_grid_notify_new_main_panel_state(window->grid, main, activated); + +} diff --git a/src/gui/window.h b/src/gui/window.h new file mode 100644 index 0000000..077d51a --- /dev/null +++ b/src/gui/window.h @@ -0,0 +1,61 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * window.h - prototypes pour la construction d'une fenêtre graphique principale + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_WINDOW_H +#define _GUI_WINDOW_H + + +#include <gtk/gtk.h> + + +#include "../glibext/helpers.h" +#include "../gtkext/panel.h" +#include "../gtkext/statusstack.h" + + + +/* Définition d'un identifiant unique */ +#define FRAMEWORK_WINDOW_ID "re.chrysalide.framework.gui" + + + +#define GTK_TYPE_FRAMEWORK_WINDOW (gtk_framework_window_get_type()) + +DECLARE_GTYPE(GtkFrameworkWindow, gtk_framework_window, GTK, FRAMEWORK_WINDOW); + + +/* Crée une nouvelle application principale pour Chrysalide. */ +GtkApplicationWindow *gtk_framework_window_new(GtkApplication *); + +/* Fournit une référence à la barre de statut intégrée. */ +GtkStatusStack *gtk_framework_window_get_status_stack(const GtkFrameworkWindow *); + +/* Ajoute un panneau à la fenêtre principale de Chrysalide. */ +void gtk_framework_window_add(GtkFrameworkWindow *, GtkTiledPanel *); + +/* Note un ajout ou un retrait de panneau principal. */ +void gtk_framework_window_notify_new_main_panel_state(GtkFrameworkWindow *, GtkTiledPanel *, bool); + + + +#endif /* _GUI_WINDOW_H */ diff --git a/src/gui/window.ui b/src/gui/window.ui new file mode 100644 index 0000000..1c6a89a --- /dev/null +++ b/src/gui/window.ui @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <menu id="main_menu_model"> + <section> + <item> + <attribute name="label" translatable="yes">Preferences</attribute> + <attribute name="action">win.preferences</attribute> + </item> + <item> + <attribute name="label" translatable="yes">About</attribute> + <attribute name="action">win.about</attribute> + </item> + </section> + </menu> + + <template class="GtkFrameworkWindow" parent="GtkApplicationWindow"> + <property name="title" translatable="no">Chrysalide</property> + <property name="default-width">800</property> + <property name="default-height">600</property> + <property name="icon-name">chrysalide-logo</property> + + <child type="titlebar"> + <object class="GtkHeaderBar"> + + <child type="start"> + <object class="GtkToggleButton"> + <property name="icon-name">dock-station-left-symbolic</property> + <property name="action-name">win.toggle-left</property> + <property name="active" bind-source="grid" bind-property="visible-left" bind-flags="sync-create"/> + </object> + </child> + + <child type="end"> + <object class="GtkToggleButton"> + <property name="icon-name">dock-station-right-symbolic</property> + <property name="action-name">win.toggle-right</property> + <property name="active" bind-source="grid" bind-property="visible-right" bind-flags="sync-create"/> + </object> + </child> + + <child type="end"> + <object class="GtkMenuButton"> + <property name="icon-name">open-menu-symbolic</property> + <property name="menu-model">main_menu_model</property> + </object> + </child> + + </object> + </child> + + <child> + <object class="GtkBox"> + <property name="orientation">vertical</property> + + <child> + <object class="GtkTilingGrid" id="grid"> + <property name="vexpand">TRUE</property> + </object> + </child> + + <child> + <object class="GtkSeparator"> + <property name="orientation">horizontal</property> + </object> + </child> + + <child> + <object class="GtkStatusStack" id="status"> + <property name="show-bottom" bind-source="grid" bind-property="visible-bottom" bind-flags="sync-create"/> + </object> + </child> + + </object> + </child> + + </template> + +</interface> diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index dd191fa..fa65484 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -1,13 +1,18 @@ noinst_LTLIBRARIES = libplugins.la -libplugins_la_SOURCES = \ - dt.h dt.c \ - pglist.h pglist.c \ - plugin-def.h \ - plugin-int.h \ - plugin.h plugin.c \ - self.h +libplugins_la_SOURCES = \ + manager-int.h \ + manager.h manager.c \ + native-int.h \ + native.h native.c \ + pglist.h pglist.c \ + plugin-def.h \ + plugin-int.h \ + plugin.h plugin.c \ + self.h \ + tweakable-int.h \ + tweakable.h tweakable.c libplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) diff --git a/src/plugins/dt.c b/src/plugins/dt.c deleted file mode 100644 index 28b70bb..0000000 --- a/src/plugins/dt.c +++ /dev/null @@ -1,545 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * dt.c - possibilité de créer de nouveaux types de façon dynamique - * - * Copyright (C) 2019 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "dt.h" - - -#include <assert.h> -#include <malloc.h> -#include <string.h> - - -#include "pglist.h" - - - -/* ------------------------- MODULE DE GESTION DES NOUVEAUX ------------------------- */ - - -#define G_TYPE_DYNAMIC_TYPES g_dynamic_types_get_type() -#define G_DYNAMIC_TYPES(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DYNAMIC_TYPES, GDynamicTypes)) -#define G_IS_DYNAMIC_TYPES(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DYNAMIC_TYPES)) -#define G_DYNAMIC_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DYNAMIC_TYPES, GDynamicTypesClass)) -#define G_IS_DYNAMIC_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DYNAMIC_TYPES)) -#define G_DYNAMIC_TYPES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DYNAMIC_TYPES, GDynamicTypesClass)) - - -/* Mémorisation des caractéristiques de type */ -typedef struct _type_dyn_info_t -{ - GType type; /* Identifiant unique obtenu */ - - GClassInitFunc cinit; /* Phase d'initialisation #1 */ - gconstpointer data; /* Eventuelles données utiles */ - - GInstanceInitFunc init; /* Phase d'initialisation #2 */ - -} type_dyn_info_t; - -/* Description de fichier binaire (instance) */ -typedef struct _GDynamicTypes -{ - GObject parent; /* A laisser en premier */ - - type_dyn_info_t **info; /* Liste d'informations utiles */ - size_t count; /* Taille de cette liste */ - -} GDynamicTypes; - -/* Description de fichier binaire (classe) */ -typedef struct _GDynamicTypesClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GDynamicTypesClass; - - -/* Indique le type défini pour une gestion de types dynamique. */ -static GType g_dynamic_types_get_type(void); - -/* Initialise la classe de gestion de types dynamique. */ -static void g_dynamic_types_class_init(GDynamicTypesClass *); - -/* Initialise une gestion de types dynamique. */ -static void g_dynamic_types_init(GDynamicTypes *); - -/* Procède à l'initialisation de l'interface de typage nouveau. */ -static void g_dynamic_types_interface_init(GTypePluginClass *); - -/* Supprime toutes les références externes. */ -static void g_dynamic_types_dispose(GDynamicTypes *); - -/* Procède à la libération totale de la mémoire. */ -static void g_dynamic_types_finalize(GDynamicTypes *); - -/* Crée un nouveau gestionnaire de nouveaux types. */ -static GDynamicTypes *g_dynamic_types_new(void); - -/* Marque une augmentation des utilisations. */ -static void g_dynamic_types_use(GDynamicTypes *); - -/* Marque une diminution des utilisations. */ -static void g_dynamic_types_unuse(GDynamicTypes *); - -/* Complète la définition d'un type dynamiquement. */ -static void g_dynamic_types_complete_type(GDynamicTypes *, GType, GTypeInfo *, GTypeValueTable *); - -/* Retrouve les informations concernant un type dynamique. */ -static type_dyn_info_t *g_dynamic_types_find(const GDynamicTypes *, GType); - -/* Fournit un identifiant GLib pour un nouveau type. */ -static GType g_dynamic_types_register_type(GDynamicTypes *, GType, const char *, GClassInitFunc, gconstpointer, GInstanceInitFunc); - - - -/* ----------------------- ACCOMPAGNEMENTS DES NOUVEAUX TYPES ----------------------- */ - - -/* Encadrement des nouveaux types dérivés */ -static GDynamicTypes *_chrysalide_dtypes = NULL; - - - -/* ---------------------------------------------------------------------------------- */ -/* MODULE DE GESTION DES NOUVEAUX */ -/* ---------------------------------------------------------------------------------- */ - -/* Indique le type défini pour une gestion de types dynamique. */ -G_DEFINE_TYPE_WITH_CODE(GDynamicTypes, g_dynamic_types, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_TYPE_PLUGIN, g_dynamic_types_interface_init)); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe de gestion de types dynamique. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_class_init(GDynamicTypesClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_dynamic_types_dispose; - object->finalize = (GObjectFinalizeFunc)g_dynamic_types_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : types = instance à initialiser. * -* * -* Description : Initialise une gestion de types dynamique. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_init(GDynamicTypes *types) -{ - -} - -/****************************************************************************** -* * -* Paramètres : iface = interface GLib à initialiser. * -* * -* Description : Procède à l'initialisation de l'interface de typage nouveau. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_interface_init(GTypePluginClass *iface) -{ - iface->use_plugin = (GTypePluginUse)g_dynamic_types_use; - iface->unuse_plugin = (GTypePluginUnuse)g_dynamic_types_unuse; - iface->complete_type_info = (GTypePluginCompleteTypeInfo)g_dynamic_types_complete_type; - -} - -/****************************************************************************** -* * -* Paramètres : types = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_dispose(GDynamicTypes *types) -{ - size_t i; /* Boucle de parcours */ - type_dyn_info_t *info; /* Information à exploiter */ - gpointer g_class; /* Classe à oublier */ - - for (i = 0; i < types->count; i++) - { - info = types->info[i]; - - if (info->type != G_TYPE_INVALID) - { - g_class = g_type_class_peek(info->type); - g_type_class_unref(g_class); - - info->type = G_TYPE_INVALID; - - } - - } - - G_OBJECT_CLASS(g_dynamic_types_parent_class)->dispose(G_OBJECT(types)); - -} - - -/****************************************************************************** -* * -* Paramètres : types = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_finalize(GDynamicTypes *types) -{ - size_t i; /* Boucle de parcours */ - - for (i = 0; i < types->count; i++) - free(types->info[i]); - - if (types->info != NULL) - free(types->info); - - G_OBJECT_CLASS(g_dynamic_types_parent_class)->finalize(G_OBJECT(types)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée un nouveau gestionnaire de nouveaux types. * -* * -* Retour : Instance mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GDynamicTypes *g_dynamic_types_new(void) -{ - GDynamicTypes *result; /* Adresse à retourner */ - - result = g_object_new(G_TYPE_DYNAMIC_TYPES, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* * -* Description : Marque une augmentation des utilisations. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_use(GDynamicTypes *types) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* * -* Description : Marque une diminution des utilisations. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_unuse(GDynamicTypes *types) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* type = nouveau type GLib à traiter. * -* info = information concernant ce type à constituer. [OUT] * -* table = table de valeur à éventuellement initialiser. [OUT] * -* * -* Description : Complète la définition d'un type dynamiquement. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_complete_type(GDynamicTypes *types, GType type, GTypeInfo *info, GTypeValueTable *table) -{ - type_dyn_info_t *nfo; /* Source d'inspiration */ - GType parent; /* Type parent du type */ - GTypeQuery query; /* Informations complémentaires*/ - - /* Consultation */ - - nfo = g_dynamic_types_find(types, type); - assert(nfo != NULL); - - parent = g_type_parent(type); - g_type_query(parent, &query); - - /* Définition */ - - info->class_size = query.class_size; - info->class_init = nfo->cinit; - info->class_data = nfo->data; - - info->instance_size = query.instance_size; - info->instance_init = nfo->init; - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* type = identifiant du type GLib à considérer. * -* * -* Description : Retrouve les informations concernant un type dynamique. * -* * -* Retour : Structure contenant les informations associées au type. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static type_dyn_info_t *g_dynamic_types_find(const GDynamicTypes *types, GType target) -{ - type_dyn_info_t *result; /* Informations à retourner */ - size_t i; /* Boucle de parcours */ - - result = NULL; - - for (i = 0; i < types->count && result == NULL; i++) - if (types->info[i]->type == target) - result = types->info[i]; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* name = désignation du nouveau type. * -* cinit = procédure d'initialisation de la classe associée. * -* data = éventuelles données à associer à la future classe. * -* init = procédure d'initialisation pour chaque instance. * -* * -* Description : Fournit un identifiant GLib pour un nouveau type. * -* * -* Retour : identifiant d'un nouveau type valide, ou G_TYPE_INVALID. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GType g_dynamic_types_register_type(GDynamicTypes *types, GType parent, const char *name, GClassInitFunc cinit, gconstpointer data, GInstanceInitFunc init) -{ - GType result; /* Identifiant à retourner */ - type_dyn_info_t *new; /* Mémorisation de paramètres */ - - /* Création d'un nouveau type adapté */ - - result = g_type_register_dynamic(parent, name, G_TYPE_PLUGIN(types), 0); - - if (result == G_TYPE_INVALID) - goto exit; - - new = malloc(sizeof(type_dyn_info_t)); - - new->type = result; - - new->cinit = cinit; - new->data = data; - - new->init = init; - - /* Inscription définitive */ - - types->info = realloc(types->info, ++types->count * sizeof(type_dyn_info_t *)); - - types->info[types->count - 1] = new; - - exit: - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* ACCOMPAGNEMENTS DES NOUVEAUX TYPES */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Lance le support de dérivations de types dans Chrysalide. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool init_chrysalide_dynamic_types(void) -{ - bool result; /* Bilan à retourner */ - - _chrysalide_dtypes = g_dynamic_types_new(); - - result = (_chrysalide_dtypes != NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Arrête le support de dérivations de types dans Chrysalide. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void exit_chrysalide_dynamic_types(void) -{ - g_object_unref(G_OBJECT(_chrysalide_dtypes)); - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* name = désignation du nouveau type. * -* cinit = procédure d'initialisation de la classe associée. * -* data = éventuelles données à associer à la future classe. * -* init = procédure d'initialisation pour chaque instance. * -* * -* Description : Fournit un identifiant GLib pour un nouveau type. * -* * -* Retour : Identifiant d'un nouveau type valide, ou G_TYPE_INVALID. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GType build_dynamic_type(GType parent, const char *name, GClassInitFunc cinit, gconstpointer data, GInstanceInitFunc init) -{ - GType result; /* Identifiant à retourner */ - - result = g_type_from_name(name); - - if (result == 0) - result = g_dynamic_types_register_type(_chrysalide_dtypes, parent, name, cinit, data, init); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : type = type d'instance à créer. * -* * -* Description : Crée un objet à partir d'un type, dynamique ou classique. * -* * -* Retour : Instance d'objet mise en place ou NULL en cas d'erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -gpointer create_object_from_type(GType type) -{ - GObject *result; /* Instance à retourner */ - - result = NULL; - - if (g_dynamic_types_find(_chrysalide_dtypes, type) != NULL) - result = build_type_instance(type); - - else - result = g_object_new(type, NULL); - - assert(result != NULL); - - return result; - -} diff --git a/src/plugins/manager-int.h b/src/plugins/manager-int.h new file mode 100644 index 0000000..dbd1d69 --- /dev/null +++ b/src/plugins/manager-int.h @@ -0,0 +1,54 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * manager-int.h - définitions internes propres aux interventions dans la gestion des extensions + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_MANAGER_INT_H +#define _PLUGINS_MANAGER_INT_H + + +#include "manager.h" + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Accompagne la fin du chargement des modules natifs. */ +typedef void (* handle_native_plugins_cb) (GPluginManager *); + +/* Prend acte du chargement de l'ensemble des greffons. */ +typedef void (* handle_all_plugins_cb) (GPluginManager *); + + +/* Accompagnant dans la gestion des extensions (interface) */ +struct _GPluginManagerInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + handle_native_plugins_cb handle_native; /* Greffons natifs chargés */ + handle_all_plugins_cb handle_all; /* Ensemble des greffons chargé*/ + +}; + + + +#endif /* _PLUGINS_MANAGER_INT_H */ diff --git a/src/plugins/manager.c b/src/plugins/manager.c new file mode 100644 index 0000000..381fbc1 --- /dev/null +++ b/src/plugins/manager.c @@ -0,0 +1,113 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * manager.c - intervention dans la gestion des extensions + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "manager.h" + + +#include "manager-int.h" + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Procède à l'initialisation de l'interface de gestion. */ +static void g_plugin_manager_default_init(GPluginManagerInterface *); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type d'une interface pour l'intervention dans la gestion des greffons. */ +G_DEFINE_INTERFACE(GPluginManager, g_plugin_manager, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de gestion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_plugin_manager_default_init(GPluginManagerInterface *iface) +{ + iface->handle_native = NULL; + iface->handle_all = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : manager = interface à manipuler. * +* * +* Description : Accompagne la fin du chargement des modules natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_plugin_manager_handle_native_plugins_loaded_event(GPluginManager *manager) +{ + GPluginManagerInterface *iface; /* Interface utilisée */ + + iface = G_PLUGIN_MANAGER_GET_IFACE(manager); + + if (iface->handle_native != NULL) + iface->handle_native(manager); + +} + + +/****************************************************************************** +* * +* Paramètres : manager = interface à manipuler. * +* * +* Description : Prend acte du chargement de l'ensemble des greffons. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_plugin_manager_handle_all_plugins_loaded_event(GPluginManager *manager) +{ + GPluginManagerInterface *iface; /* Interface utilisée */ + + iface = G_PLUGIN_MANAGER_GET_IFACE(manager); + + if (iface->handle_all != NULL) + iface->handle_all(manager); + +} diff --git a/src/plugins/manager.h b/src/plugins/manager.h new file mode 100644 index 0000000..2eb90a8 --- /dev/null +++ b/src/plugins/manager.h @@ -0,0 +1,61 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * manager.h - prototypes pour l'intervention dans la gestion des extensions + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_MANAGER_H +#define _PLUGINS_MANAGER_H + + +#include "../glibext/helpers.h" + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +#define G_TYPE_PLUGIN_MANAGER (g_plugin_manager_get_type()) + +DECLARE_INTERFACE(GPluginManager, g_plugin_manager, G, PLUGIN_MANAGER); + + +/* Accompagne la fin du chargement des modules natifs. */ +void g_plugin_manager_handle_native_plugins_loaded_event(GPluginManager *); + +/* Prend acte du chargement de l'ensemble des greffons. */ +void g_plugin_manager_handle_all_plugins_loaded_event(GPluginManager *); + + + +/* -------------------- SOLLICITATION DES FONCTIONNALITES CREEES -------------------- */ + + +#define notify_native_plugins_loaded() \ + process_all_plugins_for(G_TYPE_PLUGIN_MANAGER, G_PLUGIN_MANAGER, \ + g_plugin_manager_handle_native_plugins_loaded_event) + +#define notify_all_plugins_loaded() \ + process_all_plugins_for(G_TYPE_PLUGIN_MANAGER, G_PLUGIN_MANAGER, \ + g_plugin_manager_handle_all_plugins_loaded_event) + + + +#endif /* _PLUGINS_MANAGER_H */ diff --git a/src/plugins/native-int.h b/src/plugins/native-int.h new file mode 100644 index 0000000..575994f --- /dev/null +++ b/src/plugins/native-int.h @@ -0,0 +1,73 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * native-int.h - prototypes pour les structures internes des greffons natifs + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_NATIVE_INT_H +#define _PLUGINS_NATIVE_INT_H + + +#include "native.h" + + +#include "plugin-int.h" + + + +/* Marqueur identifiable */ +#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de + + +/* Greffon natif pour Chrysalide (instance) */ +struct _GNativePlugin +{ + GPluginModule parent; /* A laisser en premier */ + + /** + * Le module porte le code et les données en mémoire. + * + * Les fonctions *_dispose() et *_finalize() accompagnant la libération des + * greffons de la mémoire ne peuvent donc pas libérer ce module car elles + * scieraient la branche sur laquelle elles se trouvent. + * + * Par ailleurs, même s'ils sont conservés dans chaque greffon, les modules + * sont mis en place dans le code principal. C'est donc ce dernier qui les + * libère, dans la fonction on_plugin_ref_toggle(). + */ + GModule *module; /* Structure de chargement GLib*/ + +}; + + +/* Greffon natif pour Chrysalide (classe) */ +struct _GNativePluginClass +{ + GPluginModuleClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un greffon natif. */ +bool g_native_plugin_create(GNativePlugin *, const char *, const char *, const char *, const char *, const char * const *, size_t, GModule *); + + + +#endif /* _PLUGINS_NATIVE_INT_H */ diff --git a/src/plugins/native.c b/src/plugins/native.c new file mode 100644 index 0000000..de20abe --- /dev/null +++ b/src/plugins/native.c @@ -0,0 +1,295 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * native.c - interactions avec un greffon natif donné + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "native.h" + + +#include "native-int.h" +#include "../common/cpp.h" + + + +/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */ + + +/* Initialise la classe des greffons natifs. */ +static void g_native_plugin_class_init(GNativePluginClass *); + +/* Initialise une instance de greffon natif. */ +static void g_native_plugin_init(GNativePlugin *); + +/* Supprime toutes les références externes. */ +static void g_native_plugin_dispose(GNativePlugin *); + +/* Procède à la libération totale de la mémoire. */ +static void g_native_plugin_finalize(GNativePlugin *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Pointe le fichier contenant le greffon manipulé. */ +static char *g_native_plugin_get_filename(const GNativePlugin *); + +/* Fournit le nom brut associé au greffon. */ +static char *g_native_plugin_get_modname(const GNativePlugin *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon Python. */ +G_DEFINE_TYPE(GNativePlugin, g_native_plugin, G_TYPE_PLUGIN_MODULE); + + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_native_plugin_class_init(GNativePluginClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_native_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_native_plugin_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->get_filename = (get_plugin_filename_fc)g_native_plugin_get_filename; + plugin->get_modname = (get_plugin_modname_fc)g_native_plugin_get_modname; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon natif. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_native_plugin_init(GNativePlugin *plugin) +{ + plugin->module = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_native_plugin_dispose(GNativePlugin *plugin) +{ + G_OBJECT_CLASS(g_native_plugin_parent_class)->dispose(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_native_plugin_finalize(GNativePlugin *plugin) +{ + G_OBJECT_CLASS(g_native_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* name = nom du greffon pour référence, principalement. * +* desc = présentation éventuelle à destination humaine. * +* version = indication de version éventuelle. * +* url = référence vers une ressource en ligne. * +* required = liste de dépendances éventuelles ou NULL. * +* count = taille de cette liste. * +* module = extension vue du système. * +* * +* Description : Met en place un greffon natif. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_native_plugin_create(GNativePlugin *plugin, const char *name, const char *desc, const char *version, const char *url, const char * const *required, size_t count, GModule *module) +{ + bool result; /* Bilan à retourner */ + + result = g_plugin_module_create(G_PLUGIN_MODULE(plugin), name, desc, version, url, required, count); + + if (result) + plugin->module = module; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Renvoie la structure opaque associée au module en mémoire. * +* * +* Retour : Structure de chargement côté GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GModule *g_native_plugin_get_module(const GNativePlugin *plugin) +{ + GModule *result; /* Accès au module à renvoyer */ + + result = plugin->module; + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Pointe le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_native_plugin_get_filename(const GNativePlugin *plugin) +{ + char *result; /* Chemin d'accès à renvoyer */ + + result = strdup(g_module_name(plugin->module)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à valider. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_native_plugin_get_modname(const GNativePlugin *plugin) +{ + char *result; /* Désignation brute à renvoyer*/ + char *path; /* Chemin à traiter */ + char *filename; /* Nom de bibliothèque partagée*/ + size_t length; /* Taille du nom */ + int ret; /* Bilan d'une comparaison */ + + path = g_native_plugin_get_filename(plugin); + + filename = basename(path); + + if (strncmp(filename, "lib", 3) == 0) + filename += 3; + + length = strlen(filename); + +#ifdef _WIN32 +# define SHARED_SUFFIX ".dll" +#else +# define SHARED_SUFFIX ".so" +#endif + + if (length >= STATIC_STR_SIZE(SHARED_SUFFIX)) + { + ret = strncmp(&filename[length - STATIC_STR_SIZE(SHARED_SUFFIX)], + SHARED_SUFFIX, + STATIC_STR_SIZE(SHARED_SUFFIX)); + + if (ret == 0) + filename[length - STATIC_STR_SIZE(SHARED_SUFFIX)] = '\0'; + + } + + result = strdup(filename); + + free(path); + + return result; + +} diff --git a/src/plugins/native.h b/src/plugins/native.h new file mode 100644 index 0000000..18039c8 --- /dev/null +++ b/src/plugins/native.h @@ -0,0 +1,46 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * native.h - prototypes pour les interactions avec un greffon natif donné + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_NATIVE_H +#define _PLUGINS_NATIVE_H + + +#include <gmodule.h> + + +#include "../glibext/helpers.h" + + + +#define G_TYPE_NATIVE_PLUGIN (g_native_plugin_get_type()) + +DECLARE_GTYPE(GNativePlugin, g_native_plugin, G, NATIVE_PLUGIN); + + +/* Renvoie la structure opaque associée au module en mémoire. */ +GModule *g_native_plugin_get_module(const GNativePlugin *); + + + +#endif /* _PLUGINS_NATIVE_H */ diff --git a/src/plugins/pglist.c b/src/plugins/pglist.c index 6b36d2f..3e107b8 100644 --- a/src/plugins/pglist.c +++ b/src/plugins/pglist.c @@ -30,19 +30,29 @@ #include <malloc.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <i18n.h> -#include "dt.h" +#include "manager.h" +#include "native.h" #include "plugin-int.h" +#include "../common/cpp.h" #include "../common/extstr.h" #include "../core/logs.h" +#include "../core/nox.h" #include "../core/paths.h" +/** + * Prototype de la fonction de création, à garder synchronisé avec + * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h). + */ +typedef GPluginModule * (* get_plugin_instance_cb) (GModule *); + /* Liste de l'ensemble des greffons */ static GPluginModule **_pg_list = NULL; static size_t _pg_count = 0; @@ -60,6 +70,9 @@ static void browse_directory_for_plugins(const char *); /* Suit les variations du compteur de références d'un greffon. */ static void on_plugin_ref_toggle(gpointer, GPluginModule *, gboolean); +/* Fournit le greffon répondant à un nom donné. */ +static GPluginModule *_find_plugin_by_name(const char *, size_t *); + /****************************************************************************** @@ -82,8 +95,7 @@ bool init_all_plugins(bool load) char *saveptr; /* Sauvegarde pour parcours */ char *udir; /* Répertoire supplémentaire ? */ - result = init_chrysalide_dynamic_types(); - if (!result) goto exit; + result = true; g_rw_lock_init(&_pg_lock); @@ -107,8 +119,6 @@ bool init_all_plugins(bool load) if (load) load_remaning_plugins(); - exit: - return result; } @@ -129,64 +139,22 @@ bool init_all_plugins(bool load) void exit_all_plugins(void) { size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Définition du greffon */ lock_plugin_list_for_reading(); - if (_pg_list != NULL) + for (i = 0; i < _pg_count; i++) { - for (i = 0; i < _pg_count; i++) - { - assert(_pg_list[i] != NULL); - - /** - * Si le greffon a conduit à la mise en place d'autres greffons, le - * système de dépendances ne suffit pas pour le décompte des références : - * le greffon voit à un instant T son compteur décroître ici ; à un - * instant T+1, un greffon fils décrémente à son tour le compteur vers - * le greffon principal. - * - * Le compteur du conteneur tombe alors à 0, et le code correspondant - * est retiré. Lorsque que le flot d'exécution revient à la procédure - * de sortie du second greffon, son code n'est plus en mémoire. - * - * On s'assure donc que les greffons qui génèrent d'autres greffons - * sont bien traités en dernier. - */ - - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (pg_iface != NULL && pg_iface->container) - g_object_ref(_pg_list[i]); - - g_object_unref(_pg_list[i]); - - } - - for (i = 0; i < _pg_count; i++) - { - if (_pg_list[i] == NULL) - continue; - - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (pg_iface == NULL || !pg_iface->container) - continue; - - g_object_unref(_pg_list[i]); - - } + assert(_pg_list[i] != NULL); + unref_object(_pg_list[i]); + } + if (_pg_list != NULL) free(_pg_list); - } - unlock_plugin_list_for_reading(); g_rw_lock_clear(&_pg_lock); - exit_chrysalide_dynamic_types(); - } @@ -243,63 +211,95 @@ static int filter_dirs_or_mods(const struct dirent *entry) * * * Paramètres : dir = répertoire à parcourir en quête de greffons (sans /). * * * -* Description : Part à la recherche de greffons sous forme de modules. * +* Description : Indique la version (NOX/UI) associée à un nom de fichier. * * * -* Retour : - * +* Retour : true si la version complémentaire existe ou false. * * * * Remarques : - * * * ******************************************************************************/ -static void browse_directory_for_plugins(const char *dir) +static bool check_for_plugin_versions(const char *dir, const char *filename, bool *is_nox, bool *is_ui) { - struct dirent **namelist; /* Eléments trouvés */ - int ret; /* Bilan du parcours */ - char *filename; /* Elément à ausculter */ - GPluginModule *plugin; /* Greffon à intégrer ou pas */ + bool result; /* Bilan à renvoyer */ + size_t length; /* Taille du nom de fichier */ + char *alt_path; /* Autre chemin complet testé */ + int ret; /* Bilan d'une impression */ - ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort); - if (ret < 0) - { - LOG_ERROR_N("scandir"); - return; - } +#ifdef _WIN32 +# define SHARED_SUFFIX ".dll" +#else +# define SHARED_SUFFIX ".so" +#endif +#define UI_SHARED_SUFFIX "ui" SHARED_SUFFIX + + result = false; + + /* Propriétés du fichier courant */ - while (ret--) + length = strlen(filename); + + if (length < STATIC_STR_SIZE(UI_SHARED_SUFFIX)) + *is_ui = false; + + else + *is_ui = (strcmp(filename + length - STATIC_STR_SIZE(UI_SHARED_SUFFIX), UI_SHARED_SUFFIX) == 0); + + if (*is_ui) + *is_nox = false; + + else { - filename = (char *)calloc(strlen(dir) + 1 + strlen(namelist[ret]->d_name) + 1, sizeof(char)); + if (length < STATIC_STR_SIZE(SHARED_SUFFIX)) + *is_nox = false; - strcpy(filename, dir); - strcat(filename, G_DIR_SEPARATOR_S); - strcat(filename, namelist[ret]->d_name); + else + *is_nox = (strcmp(filename + length - STATIC_STR_SIZE(SHARED_SUFFIX), SHARED_SUFFIX) == 0); - if (namelist[ret]->d_type == DT_DIR) - browse_directory_for_plugins(filename); + } + + /* Recherche d'une version alternative */ + + if (*is_nox || *is_ui) + { + if (*is_nox) + ret = asprintf(&alt_path, "%s%s%.*s%s", + dir, G_DIR_SEPARATOR_S, + (int)(length - STATIC_STR_SIZE(SHARED_SUFFIX)), filename, + UI_SHARED_SUFFIX); else + ret = asprintf(&alt_path, "%s%s%.*s%s", + dir, G_DIR_SEPARATOR_S, + (int)(length - STATIC_STR_SIZE(SHARED_SUFFIX)), filename, + SHARED_SUFFIX); + + if (ret <= 0) { - plugin = g_plugin_module_new(filename); + LOG_ERROR_N("asprintf"); + goto exit; + } - if (plugin != NULL) - register_plugin(plugin); + ret = access(alt_path, R_OK | X_OK); - } + result = (ret == 0); - free(filename); - free(namelist[ret]); + free(alt_path); } - free(namelist); + exit: + + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à ajouter aux autres disponibles. * +* Paramètres : dir = répertoire à parcourir en quête de greffons (sans /). * * * -* Description : Ajoute un greffon à la liste principale de greffons. * +* Description : Part à la recherche de greffons sous forme de modules. * * * * Retour : - * * * @@ -307,52 +307,91 @@ static void browse_directory_for_plugins(const char *dir) * * ******************************************************************************/ -void _register_plugin(GPluginModule *plugin) +static void browse_directory_for_plugins(const char *dir) { - size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Informations à consulter */ - const char *name; /* Désignation du greffon */ - - /** - * L'appel sans verrou n'est fourni que pour les greffons - * mettant en place des greffons en interne ! - */ - - /* Recherche d'un éventuel doublon */ + struct dirent **namelist; /* Eléments trouvés */ + int ret; /* Bilan d'un appel */ + int k; /* Boucle de parcours */ + bool nox_mode; /* Absence de support graphique*/ + char *filename; /* Elément à ausculter */ + bool is_nox; /* Chemin de version basique ? */ + bool is_ui; /* Chemin de version graphique */ + bool has_alt; /* Existence d'une alternative */ + GModule *module; /* Abstration de manipulation */ + get_plugin_instance_cb get_instance; /* Point d'entrée exporté */ + GPluginModule *plugin; /* Greffon à intégrer ou pas */ - pg_iface = g_plugin_module_get_interface(plugin); + ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort); + if (ret < 0) + { + LOG_ERROR_N("scandir"); + return; + } - name = pg_iface->name; + nox_mode = run_in_nox_mode(); - for (i = 0; i < _pg_count; i++) + for (k = ret; k--; ) { - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (strcmp(name, pg_iface->name) == 0) + ret = asprintf(&filename, "%s%s%s", dir, G_DIR_SEPARATOR_S, namelist[k]->d_name); + if (ret <= 0) { - log_variadic_message(LMT_ERROR, - _("Plugin '%s' already registered!"), name); + LOG_ERROR_N("asprintf"); + continue; + } - break; + if (namelist[k]->d_type == DT_DIR) + browse_directory_for_plugins(filename); - } + else + { + has_alt = check_for_plugin_versions(dir, namelist[k]->d_name, &is_nox, &is_ui); - } + if ((nox_mode && is_nox) || (!nox_mode && ((is_nox && !has_alt) || is_ui))) + { + module = g_module_open(filename, G_MODULE_BIND_LAZY); + if (module == NULL) + { + log_variadic_message(LMT_ERROR, + _("Error while loading the plugin candidate '%s' : %s"), + filename, g_module_error()); + goto next_file; + } + + get_instance = NULL; + + if (!g_module_symbol(module, "get_chrysalide_plugin_instance", (gpointer *)&get_instance)) + log_variadic_message(LMT_ERROR, + _("No '%s' entry in plugin candidate '%s'"), + "<sym>", filename); + + if (get_instance == NULL) + plugin = NULL; + else + plugin = get_instance(module); + + if (plugin != NULL) + { + register_plugin(plugin); + unref_object(plugin); + } + + else + g_module_close(module); - /* Ajout du greffon à la liste */ + } + else + log_variadic_message(LMT_INFO, _("Skipping unsuitable file for plugin: %s"), filename); - if (i == _pg_count) - { - _pg_list = (GPluginModule **)realloc(_pg_list, ++_pg_count * sizeof(GPluginModule)); + } - _pg_list[_pg_count - 1] = plugin; + next_file: - g_object_add_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + free(filename); + free(namelist[k]); } - else - /* FIXME : leak(plugin); */; + free(namelist); } @@ -373,24 +412,55 @@ void _register_plugin(GPluginModule *plugin) static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolean last) { - const plugin_interface *pg_iface; /* Vitrine d'un greffon */ + const char *name; /* Désignation du greffon */ size_t index; /* Indice du greffon */ GPluginModule *same; /* Juste pour la récupération */ + GModule *module; /* Structure de chargement GLib*/ if (last) { assert(g_rw_lock_writer_trylock(&_pg_lock) == FALSE); - pg_iface = g_plugin_module_get_interface(plugin); + name = g_plugin_module_get_name(plugin); + + /** + * Les mécanismes de g_object_unref() prennent en compte la bascule d'un + * compteur de références initialement à 2 avant appel pour déclencher + * cet appel à on_plugin_ref_toggle() mis en place par g_object_add_toggle_ref(). + * + * Incrémenter ce compteur à nouveau, via get_plugin_by_name(), puis le + * décrémenter ensuite via unref_object() va conduire à une nouvelle + * bascule des statuts de suivi dans g_object_unref(). + * + * Il est ainsi impératif de rechercher une instance du greffon dans + * la liste des extensions sans toucher au compteur de références. + */ + + same = _find_plugin_by_name(name, &index); - same = get_plugin_by_name(pg_iface->name, &index); assert(same != NULL); + assert(same == plugin); _pg_list[index] = NULL; - g_object_remove_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + /** + * Suppression de la dernière référence. + */ - g_object_unref(G_OBJECT(same)); + if (G_IS_NATIVE_PLUGIN(plugin)) + module = g_native_plugin_get_module(G_NATIVE_PLUGIN(plugin)); + else + module = NULL; + + g_object_remove_toggle_ref(G_OBJECT(same), (GToggleNotify)on_plugin_ref_toggle, NULL); + + /** + * Plus aucun code issu du greffon n'est désormais utile. Le module associé peut + * être libéré de la mémoire. + */ + + if (module != NULL) + g_module_close(module); } @@ -411,9 +481,40 @@ static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolea void register_plugin(GPluginModule *plugin) { + size_t i; /* Boucle de parcours */ + const char *name; /* Désignation du greffon */ + const char *existing; /* Nom d'un greffon en place */ + g_rw_lock_writer_lock(&_pg_lock); - _register_plugin(plugin); + /* Recherche d'un éventuel doublon */ + + name = g_plugin_module_get_name(plugin); + + for (i = 0; i < _pg_count; i++) + { + existing = g_plugin_module_get_name(_pg_list[i]); + + if (strcmp(name, existing) == 0) + { + log_variadic_message(LMT_ERROR, _("Plugin '%s' already registered!"), name); + break; + } + + } + + /* Ajout du greffon à la liste */ + + if (i == _pg_count) + { + _pg_list = realloc(_pg_list, ++_pg_count * sizeof(GPluginModule)); + + _pg_list[_pg_count - 1] = plugin; + ref_object(plugin); + + g_object_add_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + + } g_rw_lock_writer_unlock(&_pg_lock); @@ -481,13 +582,18 @@ void load_remaning_plugins(void) /* Supprime les greffons non chargés */ - for (i = 0; i < _pg_count; i++) + for (i = 0; i < _pg_count;) { flags = g_plugin_module_get_flags(_pg_list[i]); - if ((flags & PSF_LOADED) == 0) + if (flags & PSF_LOADED) + i++; + + else { - g_object_unref(G_OBJECT(_pg_list[i])); + unref_object(_pg_list[i]); + + assert(_pg_list[i] == NULL); memmove(&_pg_list[i], &_pg_list[i + 1], (_pg_count - i - 1) * sizeof(GPluginModule *)); _pg_count--; @@ -514,15 +620,16 @@ void load_remaning_plugins(void) * * * Retour : Instance du greffon trouvé ou NULL si aucun. * * * -* Remarques : - * +* Remarques : Le compteur de référence d'un greffon trouvé n'est pas * +* modifié. * * * ******************************************************************************/ -GPluginModule *get_plugin_by_name(const char *name, size_t *index) +static GPluginModule *_find_plugin_by_name(const char *name, size_t *index) { GPluginModule *result; /* Greffon trouvé à renvoyer */ size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Vitrine d'un greffon */ + const char *current; /* Nom du greffon courant */ result = NULL; @@ -536,9 +643,9 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) /* Si on est en train de procéder à un nettoyage... */ if (_pg_list[i] == NULL) continue; - pg_iface = g_plugin_module_get_interface(_pg_list[i]); + current = g_plugin_module_get_name(_pg_list[i]); - if (strcmp(pg_iface->name, name) == 0) + if (strcmp(current, name) == 0) { result = _pg_list[i]; @@ -549,9 +656,6 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) } - if (result != NULL) - g_object_ref(G_OBJECT(result)); - return result; } @@ -559,33 +663,25 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) /****************************************************************************** * * -* Paramètres : count = nombre de greffons trouvés. [OUT] * +* Paramètres : name = désignation du greffon recherché. * +* index = indice du greffon trouvé. [OUT] * * * -* Description : Fournit la liste de l'ensemble des greffons. * +* Description : Fournit le greffon répondant à un nom donné. * * * -* Retour : Liste de tous les greffons chargés. * +* Retour : Instance du greffon trouvé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ -GPluginModule **get_all_plugins(size_t *count) +GPluginModule *get_plugin_by_name(const char *name, size_t *index) { - GPluginModule **result; /* Liste à retourner */ - size_t i; /* Boucle de parcours */ - - g_rw_lock_reader_lock(&_pg_lock); - - result = malloc(_pg_count * sizeof(GPluginModule *)); - *count = _pg_count; + GPluginModule *result; /* Greffon trouvé à renvoyer */ - for (i = 0; i < _pg_count; i++) - { - result[i] = _pg_list[i]; - g_object_ref(G_OBJECT(_pg_list[i])); - } + result = _find_plugin_by_name(name, index); - g_rw_lock_reader_unlock(&_pg_lock); + if (result != NULL) + ref_object(result); return result; @@ -594,48 +690,30 @@ GPluginModule **get_all_plugins(size_t *count) /****************************************************************************** * * -* Paramètres : action = fonctionnalité recherchée. * -* count = nombre de greffons trouvés. [OUT] * +* Paramètres : count = nombre de greffons trouvés. [OUT] * * * -* Description : Fournit les greffons offrant le service demandé. * +* Description : Fournit la liste de l'ensemble des greffons. * * * -* Retour : Liste de greffons correspondants issue d'un tri interne. * +* Retour : Liste de tous les greffons chargés. * * * * Remarques : - * * * ******************************************************************************/ -GPluginModule **get_all_plugins_for_action(PluginAction action, size_t *count) +GPluginModule **get_all_plugins(size_t *count) { GPluginModule **result; /* Liste à retourner */ - size_t i; /* Boucle de parcours #1 */ - const plugin_interface *pg_iface; /* Informations à consulter */ - size_t j; /* Boucle de parcours #2 */ - - result = NULL; - *count = 0; + size_t i; /* Boucle de parcours */ g_rw_lock_reader_lock(&_pg_lock); + result = malloc(_pg_count * sizeof(GPluginModule *)); + *count = _pg_count; + for (i = 0; i < _pg_count; i++) { - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - for (j = 0; j < pg_iface->actions_count; j++) - { - if (pg_iface->actions[j] == action) - { - result = realloc(result, ++(*count) * sizeof(GPluginModule *)); - - result[*count - 1] = _pg_list[i]; - g_object_ref(G_OBJECT(_pg_list[i])); - - break; - - } - - } - + result[i] = _pg_list[i]; + ref_object(result[i]); } g_rw_lock_reader_unlock(&_pg_lock); diff --git a/src/plugins/pglist.h b/src/plugins/pglist.h index 83e9091..777b19c 100644 --- a/src/plugins/pglist.h +++ b/src/plugins/pglist.h @@ -52,9 +52,6 @@ void _lock_unlock_plugin_list_for_reading(bool lock); #define unlock_plugin_list_for_reading() _lock_unlock_plugin_list_for_reading(false) /* Ajoute un greffon à la liste principale de greffons. */ -void _register_plugin(GPluginModule *); - -/* Ajoute un greffon à la liste principale de greffons. */ void register_plugin(GPluginModule *); /* Charge tous les greffons restant à charger. */ @@ -66,16 +63,74 @@ GPluginModule *get_plugin_by_name(const char *, size_t *); /* Fournit la liste de l'ensemble des greffons. */ GPluginModule **get_all_plugins(size_t *); -/* Fournit les greffons offrant le service demandé. */ -GPluginModule **get_all_plugins_for_action(PluginAction, size_t *); - /** * Définitions des opérations appliquables à une catégories de greffons. */ -#define process_all_plugins_for(a, f, ...) \ +#define process_all_plugins_for(tp, cst, fc) \ + do \ + { \ + size_t __count; \ + GPluginModule **__list; \ + size_t __i; \ + GPluginModule *__pg; \ + __list = get_all_plugins(&__count); \ + for (__i = 0; __i < __count; __i++) \ + { \ + __pg = __list[__i]; \ + if (G_TYPE_CHECK_INSTANCE_TYPE(__pg, tp)) \ + fc(cst(__pg)); \ + unref_object(__pg); \ + } \ + if (__list != NULL) \ + free(__list); \ + } \ + while (0) + +#define accumulate_from_all_plugins(tp, cst, fc, atp, cnt) \ + ({ \ + atp *__acc_list; \ + size_t __count; \ + GPluginModule **__list; \ + size_t __i; \ + GPluginModule *__pg; \ + size_t __tmp_count; \ + atp *__tmp_list; \ + *cnt = 0; \ + __acc_list = NULL; \ + __list = get_all_plugins(&__count); \ + for (__i = 0; __i < __count; __i++) \ + { \ + __pg = __list[__i]; \ + if (G_TYPE_CHECK_INSTANCE_TYPE(__pg, tp)) \ + { \ + __tmp_list = fc(cst(__pg), &__tmp_count); \ + if (__tmp_list != NULL) \ + { \ + __acc_list = realloc(__acc_list, \ + (*cnt + __tmp_count) * sizeof(atp)); \ + memcpy(&__acc_list[*cnt], __tmp_list, \ + __tmp_count * sizeof(atp)); \ + *cnt += __tmp_count; \ + free(__tmp_list); \ + } \ + } \ + unref_object(__pg); \ + } \ + if (__list != NULL) \ + free(__list); \ + __acc_list; \ + }) + + + +#if 0 + +// TODO : REMME + +#define process_all_plugins_for_old__(a, f, ...) \ do \ { \ size_t __count; \ @@ -114,11 +169,13 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *); /* DPS_PG_MANAGEMENT */ +/* #define notify_native_plugins_loaded() \ - process_all_plugins_for(PGA_NATIVE_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL) + process_all_plugins_for_old__(PGA_NATIVE_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL) #define notify_all_plugins_loaded() \ - process_all_plugins_for(PGA_ALL_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL) + process_all_plugins_for_old__(PGA_ALL_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL) +*/ #define build_type_instance(t) \ process_plugins_while_null(PGA_TYPE_BUILDING, g_plugin_module_build_type_instance, t) @@ -126,44 +183,47 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *); /* DPS_SETUP */ #define include_plugin_theme(d, r, c) \ - process_all_plugins_for(PGA_GUI_THEME, g_plugin_module_include_theme, d, r, c) + process_all_plugins_for_old__(PGA_GUI_THEME, g_plugin_module_include_theme, d, r, c) /* DPS_RUNNING */ #define notify_panel_creation(i) \ - process_all_plugins_for(PGA_PANEL_CREATION, g_plugin_module_notify_panel_creation, i) + process_all_plugins_for_old__(PGA_PANEL_CREATION, g_plugin_module_notify_panel_creation, i) #define notify_panel_docking(i, d) \ - process_all_plugins_for(PGA_PANEL_DOCKING, g_plugin_module_notify_panel_docking, i, d) + process_all_plugins_for_old__(PGA_PANEL_DOCKING, g_plugin_module_notify_panel_docking, i, d) /* DPS_CONTENT */ #define handle_binary_content(a, c, i, s) \ - process_all_plugins_for(a, g_plugin_module_handle_binary_content, c, i, s) + process_all_plugins_for_old__(a, g_plugin_module_handle_binary_content, c, i, s) #define handle_loaded_content(a, c, i, s) \ - process_all_plugins_for(a, g_plugin_module_handle_loaded_content, c, i, s) + process_all_plugins_for_old__(a, g_plugin_module_handle_loaded_content, c, i, s) /* DPS_FORMAT */ #define handle_known_format_analysis(a, f, g, s) \ - process_all_plugins_for(a, g_plugin_module_handle_known_format_analysis, f, g, s) + process_all_plugins_for_old__(a, g_plugin_module_handle_known_format_analysis, f, g, s) #define preload_binary_format(a, f, i, s) \ - process_all_plugins_for(a, g_plugin_module_preload_binary_format, f, i, s) + process_all_plugins_for_old__(a, g_plugin_module_preload_binary_format, f, i, s) #define attach_debug_format(f) \ - process_all_plugins_for(PGA_FORMAT_ATTACH_DEBUG, g_plugin_module_attach_debug_format, f) + process_all_plugins_for_old__(PGA_FORMAT_ATTACH_DEBUG, g_plugin_module_attach_debug_format, f) /* DPS_DISASSEMBLY */ #define process_disassembly_event(a, b, s, c) \ - process_all_plugins_for(a, g_plugin_module_process_disassembly_event, b, s, c) + process_all_plugins_for_old__(a, g_plugin_module_process_disassembly_event, b, s, c) /* DPS_DETECTION */ #define detect_external_tools(a, cnt, v, n, c) \ - process_all_plugins_for(a, g_plugin_module_detect_external_tools, cnt, v, n, c) + process_all_plugins_for_old__(a, g_plugin_module_detect_external_tools, cnt, v, n, c) + + +#endif diff --git a/src/plugins/plugin-def.h b/src/plugins/plugin-def.h index 1118140..b5e0b51 100644 --- a/src/plugins/plugin-def.h +++ b/src/plugins/plugin-def.h @@ -35,18 +35,6 @@ /* ------------------------ IDENTIFICATION DE COMPATIBILITES ------------------------ */ -/* Version identifiant les définitions courantes */ -typedef uint32_t plugin_abi_version_t; - -#define DEFINE_PLUGIN_ABI_VERSION(maj, min, rev) \ - (((maj & 0xff) << 24) | ((min & 0xff) << 16) | (rev & 0xffff)) - -#define GET_ABI_MAJ_VERSION(vs) ((vs >> 24) & 0xff) -#define GET_ABI_MIN_VERSION(vs) ((vs >> 16) & 0xff) -#define GET_ABI_REV_VERSION(vs) (vs & 0xffff) - -#define CURRENT_ABI_VERSION DEFINE_PLUGIN_ABI_VERSION(0, 3, 0) - /* ------------------------- DEFINITION D'UN PROJET INTERNE ------------------------- */ @@ -232,14 +220,14 @@ typedef enum _PluginAction /* ------------------------ PREMIER INTERFACAGE PROTOCOLAIRE ------------------------ */ -#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de1234abcdull +//#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de1234abcdull /* Définition d'un greffon */ typedef struct _plugin_interface { uint64_t magic; /* Vérification a minima */ - plugin_abi_version_t abi_version; /* Version du protocole utilisé*/ + uint32_t /*plugin_abi_version_t*/ abi_version; /* Version du protocole utilisé*/ /** * Les champs suivants ne sont généralement pas alloués dynamiquement, diff --git a/src/plugins/plugin-int.h b/src/plugins/plugin-int.h index 3ba19dc..07b455a 100644 --- a/src/plugins/plugin-int.h +++ b/src/plugins/plugin-int.h @@ -37,21 +37,28 @@ #include "../common/bits.h" -/* Transfert de la conscience de soi. */ -typedef void (* pg_set_self_fc) (GPluginModule *); -/* Prend acte du [dé]chargement du greffon. */ +/* Pointe le fichier contenant le greffon manipulé. */ +typedef char * (* get_plugin_filename_fc) (const GPluginModule *); + +/* Fournit le nom brut associé au greffon. */ +typedef char * (* get_plugin_modname_fc) (const GPluginModule *); + +/* Prend acte de (l'|la dés)activation du greffon. */ typedef bool (* pg_management_fc) (GPluginModule *); + + + +/* Transfert de la conscience de soi. */ +typedef void (* pg_set_self_fc) (GPluginModule *); + /* Accompagne la fin du chargement des modules natifs. */ typedef void (* pg_plugins_loaded_fc) (GPluginModule *, PluginAction); /* Crée une instance à partir d'un type dynamique externe. */ typedef gpointer (* pg_build_instance_fc) (GPluginModule *, PluginAction, GType); -/* Fournit le nom brut associé au greffon. */ -typedef char * (* pg_get_modname_fc) (const GPluginModule *); - #if 0 /* Procède à une opération liée à un contenu binaire. */ @@ -90,22 +97,47 @@ typedef void (* pg_detect_tools_fc) (const GPluginModule *, PluginAction, const #endif +/* Marqueur identifiable */ +#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de + + +/* Version identifiant les définitions courantes */ +typedef uint32_t plugin_abi_version_t; + +#define DEFINE_PLUGIN_ABI_VERSION(maj, min, rev) \ + (((maj & 0xff) << 24) | ((min & 0xff) << 16) | (rev & 0xffff)) + +#define GET_ABI_MAJ_VERSION(vs) ((vs >> 24) & 0xff) +#define GET_ABI_MIN_VERSION(vs) ((vs >> 16) & 0xff) +#define GET_ABI_REV_VERSION(vs) (vs & 0xffff) + +/** + * 0.3.0 : dernière version avec actions et fonctions associées + * 1.0.0 (04/01/25) : bascule en chargement d'objet et interfaces + */ +#define CURRENT_ABI_VERSION DEFINE_PLUGIN_ABI_VERSION(1, 0, 0) + + /* Greffon pour Chrysalide (instance) */ struct _GPluginModule { GObject parent; /* A laisser en premier */ - char *filename; /* Fichier associé au greffon */ - GModule *module; /* Abstration de manipulation */ + uint32_t magic; /* Vérification a minima */ + plugin_abi_version_t abi_version; /* Version du protocole utilisé*/ + + char *name; /* Désignation humaine courte */ + char *desc; /* Description plus loquace */ + char *version; /* Version du greffon */ + char *url; /* Site Web associé */ - const plugin_interface *interface; /* Déclaration d'interfaçage */ + char **required; /* Pré-chargements requis */ + size_t required_count; /* Quantité de ces dépendances */ PluginStatusFlags flags; /* Fanion pour l'état courant */ bitfield_t *dependencies; /* Cartographie des dépendances*/ - //GGenConfig *config; /* Configuration dédiée */ - }; @@ -114,6 +146,18 @@ struct _GPluginModuleClass { GObjectClass parent; /* A laisser en premier */ + get_plugin_filename_fc get_filename; /* Obtention du chemin */ + get_plugin_modname_fc get_modname; /* Fourniture du nom brut */ + + pg_management_fc enable; /* Procédure d'activation */ + pg_management_fc disable; /* Procédure d'extinction */ + + + + ///////////////////////////////////////////// + +#if 0 + pg_management_fc init; /* Procédure d'initialisation */ pg_management_fc manage; /* Etape dans la vie du greffon*/ pg_management_fc exit; /* Procédure d'extinction */ @@ -121,8 +165,6 @@ struct _GPluginModuleClass pg_plugins_loaded_fc plugins_loaded; /* Fin des chargements */ pg_build_instance_fc build_instance; /* Création d'objets */ - pg_get_modname_fc get_modname; /* Fourniture du nom brut */ - #if 0 #ifdef INCLUDE_GTK_SUPPORT pg_include_theme_fc include_theme; /* Extension d'un thème */ @@ -141,12 +183,28 @@ struct _GPluginModuleClass pg_detect_tools_fc detect; /* Lancement de détections */ #endif +#endif + + ///////////////////////////////////////////// + }; -/* Met en place la configuration dédiée au greffon. */ -void g_plugin_module_create_config(GPluginModule *); + +#define STORE_PLUGIN_ABI(p) \ + do \ + { \ + GPluginModule *_p; \ + _p = G_PLUGIN_MODULE(p); \ + _p->magic = CHRYSALIDE_PLUGIN_MAGIC; \ + _p->abi_version = CURRENT_ABI_VERSION; \ + } \ + while (0); + + +/* Met en place un greffon. */ +bool g_plugin_module_create(GPluginModule *, const char *, const char *, const char *, const char *, const char * const *, size_t); diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c index 5b3e475..dfdf3ed 100644 --- a/src/plugins/plugin.c +++ b/src/plugins/plugin.c @@ -30,14 +30,13 @@ #include <libgen.h> #include <malloc.h> #include <stdarg.h> -#include <stdbool.h> #include <stdio.h> #include <string.h> -#include "dt.h" #include "pglist.h" #include "plugin-int.h" +#include "../common/compiler.h" #include "../common/extstr.h" #include "../common/pathname.h" #include "../common/xdg.h" @@ -56,19 +55,12 @@ static void g_plugin_module_dispose(GPluginModule *); /* Procède à la libération totale de la mémoire. */ static void g_plugin_module_finalize(GPluginModule *); -/* Initialise la classe des greffons d'extension. */ -static void g_plugin_module_init_gclass(GPluginModuleClass *, GModule *); - -/* Fournit le nom brut associé au greffon. */ -static char *_g_plugin_module_get_modname(const GPluginModule *); - /* Indique le type défini pour un greffon. */ G_DEFINE_TYPE(GPluginModule, g_plugin_module, G_TYPE_OBJECT); - /****************************************************************************** * * * Paramètres : class = classe à initialiser. * @@ -84,16 +76,17 @@ G_DEFINE_TYPE(GPluginModule, g_plugin_module, G_TYPE_OBJECT); static void g_plugin_module_class_init(GPluginModuleClass *class) { GObjectClass *object; /* Autre version de la classe */ - GPluginModuleClass *plugin; /* Version parente de la classe*/ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_plugin_module_dispose; object->finalize = (GObjectFinalizeFunc)g_plugin_module_finalize; - plugin = G_PLUGIN_MODULE_CLASS(class); + class->get_filename = NULL; + class->get_modname = NULL; - plugin->get_modname = (pg_get_modname_fc)_g_plugin_module_get_modname; + class->enable = NULL; + class->disable = NULL; } @@ -112,7 +105,17 @@ static void g_plugin_module_class_init(GPluginModuleClass *class) static void g_plugin_module_init(GPluginModule *plugin) { - //plugin->config = NULL; + plugin->name = NULL; + plugin->desc = NULL; + plugin->version = NULL; + plugin->url = NULL; + + plugin->required = NULL; + plugin->required_count = 0; + + plugin->flags = PSF_NONE; + + plugin->dependencies = NULL; } @@ -131,29 +134,29 @@ static void g_plugin_module_init(GPluginModule *plugin) static void g_plugin_module_dispose(GPluginModule *plugin) { - const plugin_interface *pg_iface; /* Définition du greffon */ size_t i; /* Boucle de parcours */ + size_t index; /* Indice de greffon visé */ GPluginModule *dependency; /* Module nécessaire */ GPluginModuleClass *class; /* Classe de l'instance active */ - pg_iface = g_plugin_module_get_interface(plugin); - - if (pg_iface != NULL) + if (plugin->dependencies != NULL) { lock_plugin_list_for_reading(); - for (i = 0; i < pg_iface->required_count; i++) + for (i = 0; i < plugin->required_count; i++) { - dependency = get_plugin_by_name(pg_iface->required[i], NULL); + dependency = get_plugin_by_name(plugin->required[i], &index); - /* Si le chargement a bien été complet avant la sortie... */ - if (dependency != NULL) + /* Si la dépendance a bien été pris en compte... */ + if (test_in_bit_field(plugin->dependencies, index)) { + assert(dependency != NULL); + /* Un coup pour l'appel à get_plugin_by_name(). */ - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); /* Un coup pour la dépendance */ - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); } @@ -163,25 +166,13 @@ static void g_plugin_module_dispose(GPluginModule *plugin) } - class = G_PLUGIN_MODULE_GET_CLASS(plugin); - - if (class->exit != NULL) - class->exit(plugin); - - /* - if (plugin->config != NULL) + if (plugin->flags & PSF_LOADED) { - g_generic_config_write(plugin->config); - - g_clear_object(&plugin->config); + class = G_PLUGIN_MODULE_GET_CLASS(plugin); - } - */ + if (class->disable != NULL) + class->disable(plugin); - if (plugin->module != NULL) - { - g_module_close(plugin->module); - plugin->module = NULL; } G_OBJECT_CLASS(g_plugin_module_parent_class)->dispose(G_OBJECT(plugin)); @@ -203,7 +194,25 @@ static void g_plugin_module_dispose(GPluginModule *plugin) static void g_plugin_module_finalize(GPluginModule *plugin) { - free(plugin->filename); + size_t i; /* Boucle de parcours */ + + if (plugin->name != NULL) + free(plugin->name); + + if (plugin->desc != NULL) + free(plugin->desc); + + if (plugin->version != NULL) + free(plugin->version); + + if (plugin->url != NULL) + free(plugin->url); + + for (i = 0; i < plugin->required_count; i++) + free(plugin->required[i]); + + if (plugin->required != NULL) + free(plugin->required); if (plugin->dependencies != NULL) delete_bit_field(plugin->dependencies); @@ -215,627 +224,173 @@ static void g_plugin_module_finalize(GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : filename = nom du fichier à charger. * +* Paramètres : plugin = instance à initialiser pleinement. * +* name = nom du greffon pour référence, principalement. * +* desc = présentation éventuelle à destination humaine. * +* version = indication de version éventuelle. * +* url = référence vers une ressource en ligne. * +* required = liste de dépendances éventuelles ou NULL. * +* count = taille de cette liste. * * * -* Description : Crée un module pour un greffon donné. * +* Description : Met en place un greffon. * * * -* Retour : Adresse de la structure mise en place. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -GPluginModule *g_plugin_module_new(const gchar *filename) +bool g_plugin_module_create(GPluginModule *plugin, const char *name, const char *desc, const char *version, const char *url, const char * const *required, size_t count) { - GPluginModule *result; /* Structure à retourner */ - GModule *module; /* Abstration de manipulation */ - pg_set_self_fc set_self; /* Copie du greffon */ - const plugin_interface *interface; /* Déclaration d'interfaçage */ - plugin_abi_version_t current; /* Version de l'ABI actuelle */ - bool valid; /* Statut de validité */ - size_t i; /* Boucle de parcours */ - uint32_t action; /* Identifiant d'une action */ - uint32_t category; /* Catégorie principale */ - uint32_t sub; /* Sous-catégorie visée */ - GType gtype; /* Nouveau type de greffon */ - - module = g_module_open(filename, G_MODULE_BIND_LAZY); - if (module == NULL) - { - log_variadic_message(LMT_ERROR, - _("Error while loading the plugin candidate '%s' : %s"), - filename, g_module_error()); - goto bad_module; - } - - -#define load_plugin_symbol(mod, sym, dest) \ - ({ \ - bool __result; \ - if (!g_module_symbol(mod, sym, (gpointer *)dest)) \ - { \ - log_variadic_message(LMT_ERROR, \ - _("No '%s' entry in plugin candidate '%s'"), \ - sym, filename); \ - __result = false; \ - } \ - else __result = true; \ - __result; \ - }) - - - /* Récupération de la version d'ABI */ - - if (!load_plugin_symbol(module, "chrysalide_plugin_set_self", &set_self)) - goto no_self_setter; - - if (!load_plugin_symbol(module, "_chrysalide_plugin", &interface)) - goto no_interface; - - current = CURRENT_ABI_VERSION; - - if (current != interface->abi_version) - goto wrong_abi; - - /* Localisation des différents points d'entrée déclarés */ - + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours #1 */ + size_t k; /* Boucle de parcours #2 */ -#define check_plugin_symbol(mod, sym) \ - ({ \ - bool __result; \ - __result = g_module_symbol(mod, sym, (gpointer []) { NULL }); \ - if (!__result) \ - log_variadic_message(LMT_ERROR, \ - _("No '%s' entry in plugin candidate '%s'"), \ - sym, filename); \ - __result; \ - }) + /* Validations préalables */ + assert(name != NULL); - valid = true; + result = (name != NULL); - for (i = 0; i < interface->actions_count && valid; i++) + if (result && plugin->abi_version != CURRENT_ABI_VERSION) { - action = interface->actions[i]; - category = MASK_PLUGIN_CATEGORY(action); - sub = MASK_PLUGIN_SUB_CATEGORY(action); - - switch (category) - { - case DPC_BASIC: - - switch (sub) - { - case DPS_NONE: - break; - - case DPS_PG_MANAGEMENT: - - switch (action) - { - case PGA_PLUGIN_INIT: - valid = check_plugin_symbol(module, "chrysalide_plugin_init"); - break; - - case PGA_PLUGIN_LOADED: - valid = check_plugin_symbol(module, "chrysalide_plugin_manage"); - break; - - case PGA_PLUGIN_EXIT: - valid = check_plugin_symbol(module, "chrysalide_plugin_exit"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } - - break; - - case DPS_CORE_MANAGEMENT: - - switch (action) - { - case PGA_NATIVE_PLUGINS_LOADED: - case PGA_ALL_PLUGINS_LOADED: - valid = check_plugin_symbol(module, "chrysalide_plugin_on_plugins_loaded"); - break; - - case PGA_TYPE_BUILDING: - valid = check_plugin_symbol(module, "chrysalide_plugin_build_type_instance"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } - - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); - break; - - } - - break; - -#if 0 - - case DPC_GUI: - - switch (sub) - { - case DPS_SETUP: - - switch (action) - { - case PGA_GUI_THEME: - valid = check_plugin_symbol(module, "chrysalide_plugin_include_theme"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } - - break; - - case DPS_RUNNING: - - switch (action) - { - case PGA_PANEL_CREATION: - valid = check_plugin_symbol(module, "chrysalide_plugin_on_panel_creation"); - break; - - case PGA_PANEL_DOCKING: - valid = check_plugin_symbol(module, "chrysalide_plugin_on_panel_docking"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } - - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); - break; - - } - - break; - - case DPC_BINARY_PROCESSING: - - switch (sub) - { - case DPS_CONTENT: - - switch (action) - { - case PGA_CONTENT_EXPLORER: - case PGA_CONTENT_RESOLVER: - valid = check_plugin_symbol(module, "chrysalide_plugin_handle_binary_content"); - break; - - case PGA_CONTENT_ANALYZED: - valid = check_plugin_symbol(module, "chrysalide_plugin_handle_loaded_content"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } + result = false; - break; - - case DPS_FORMAT: + log_variadic_message(LMT_ERROR, _("ABI mismatch detected: %08x (plugin) vs %08x (core)"), + plugin->abi_version, CURRENT_ABI_VERSION); - switch (action) - { - case PGA_FORMAT_ANALYSIS_STARTED: - case PGA_FORMAT_ANALYSIS_ENDED: - case PGA_FORMAT_POST_ANALYSIS_STARTED: - case PGA_FORMAT_POST_ANALYSIS_ENDED: - valid = check_plugin_symbol(module, "chrysalide_plugin_handle_binary_format_analysis"); - break; + } - case PGA_FORMAT_PRELOAD: - valid = check_plugin_symbol(module, "chrysalide_plugin_preload_binary_format"); - break; + /* Mémorisation des informations */ - case PGA_FORMAT_ATTACH_DEBUG: - valid = check_plugin_symbol(module, "chrysalide_plugin_attach_debug"); - break; + if (result) + { + plugin->name = strdup(name); - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; + if (desc != NULL) + plugin->desc = strdup(desc); - } + if (version != NULL) + plugin->version = strdup(version); - break; + if (url != NULL) + plugin->url = strdup(url); - case DPS_DISASSEMBLY: - valid = check_plugin_symbol(module, "chrysalide_plugin_process_disassembly_event"); - break; - - case DPS_DETECTION: - valid = check_plugin_symbol(module, "chrysalide_plugin_detect_external_tools"); - break; + if (count > 0) + { + plugin->required = malloc(count * sizeof(char *)); + plugin->required_count = 0; - default: - log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); + for (i = 0; i < count; i++) + { + for (k = 0; k < plugin->required_count; k++) + if (strcmp(required[i], plugin->required[k]) == 0) break; - } + if (k < plugin->required_count) + continue; -#endif + plugin->required[plugin->required_count++] = strdup(required[i]); - break; + } - default: - log_variadic_message(LMT_WARNING, - _("Unknown category '0x%02x' in plugin '%s'..."), category, filename); - break; + plugin->required = realloc(plugin->required, plugin->required_count * sizeof(char *)); } } - if (!valid) - goto missing_feature; - - gtype = build_dynamic_type(G_TYPE_PLUGIN_MODULE, interface->gtp_name, - (GClassInitFunc)g_plugin_module_init_gclass, module, NULL); - - if (gtype == G_TYPE_INVALID) - goto no_instance; - - result = g_object_new(gtype, NULL); - - result->filename = strdup(filename); - result->module = module; - - result->interface = interface; - - set_self(result); - return result; - no_self_setter: - - log_variadic_message(LMT_ERROR, _("Self pointer setter is missing for plugin '%s'"), filename); - goto bad_plugin; - - no_interface: - - log_variadic_message(LMT_ERROR, _("Main interface is missing for plugin '%s'"), filename); - goto bad_plugin; - - wrong_abi: - - log_variadic_message(LMT_ERROR, _("ABI mismatch detected! Plugin '%s' rejected"), filename); - goto bad_plugin; - - missing_feature: - - log_variadic_message(LMT_ERROR, _("An expected feature is missing for plugin '%s'"), filename); - goto bad_plugin; - - no_instance: - - log_variadic_message(LMT_ERROR, _("Unabled to create an instance of plugin '%s'"), filename); - - bad_plugin: - - g_module_close(module); - - bad_module: - - return NULL; - } /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* module = module représentant le greffon chargé en mémoire. * +* Paramètres : plugin = greffon à consulter. * * * -* Description : Initialise la classe des greffons d'extension. * +* Description : Indique le nom associé à un greffon. * * * -* Retour : - * +* Retour : Désignation interne de l'extension, pour référence(s). * * * * Remarques : - * * * ******************************************************************************/ -static void g_plugin_module_init_gclass(GPluginModuleClass *class, GModule *module) +const char *g_plugin_module_get_name(const GPluginModule *plugin) { - const plugin_interface *interface; /* Déclaration d'interfaçage */ - size_t i; /* Boucle de parcours */ - uint32_t action; /* Identifiant d'une action */ - uint32_t category; /* Catégorie principale */ - uint32_t sub; /* Sous-catégorie visée */ - - -#undef load_plugin_symbol - -#define load_plugin_symbol(mod, sym, dest) \ - ({ \ - bool __result; \ - __result = g_module_symbol(mod, sym, (gpointer *)dest); \ - assert(__result); \ - __result; \ - }) - - - load_plugin_symbol(module, "_chrysalide_plugin", &interface); - - for (i = 0; i < interface->actions_count; i++) - { - action = interface->actions[i]; - category = MASK_PLUGIN_CATEGORY(action); - sub = MASK_PLUGIN_SUB_CATEGORY(action); - - switch (category) - { - case DPC_BASIC: - - switch (sub) - { - case DPS_NONE: - break; - - case DPS_PG_MANAGEMENT: - - switch (action) - { - case PGA_PLUGIN_INIT: - load_plugin_symbol(module, "chrysalide_plugin_init", &class->init); - break; - - case PGA_PLUGIN_LOADED: - load_plugin_symbol(module, "chrysalide_plugin_manage", &class->manage); - break; - - case PGA_PLUGIN_EXIT: - load_plugin_symbol(module, "chrysalide_plugin_exit", &class->exit); - break; - - default: - assert(false); - break; - - } - - break; - - case DPS_CORE_MANAGEMENT: - - switch (action) - { - case PGA_NATIVE_PLUGINS_LOADED: - case PGA_ALL_PLUGINS_LOADED: - load_plugin_symbol(module, "chrysalide_plugin_on_plugins_loaded", - &class->plugins_loaded); - break; - - case PGA_TYPE_BUILDING: - load_plugin_symbol(module, "chrysalide_plugin_build_type_instance", - &class->build_instance); - break; - - default: - assert(false); - break; - - } - - break; - - default: - assert(false); - break; - - } - - break; -#if 0 - case DPC_GUI: - - switch (sub) - { - case DPS_SETUP: - - switch (action) - { - case PGA_GUI_THEME: -#ifdef INCLUDE_GTK_SUPPORT - load_plugin_symbol(module, "chrysalide_plugin_include_theme", - &class->include_theme); -#endif - break; - - default: - assert(false); - break; - - } - - break; - - case DPS_RUNNING: - - switch (action) - { - case PGA_PANEL_CREATION: -#ifdef INCLUDE_GTK_SUPPORT - load_plugin_symbol(module, "chrysalide_plugin_on_panel_creation", - &class->notify_panel); -#endif - break; - - case PGA_PANEL_DOCKING: -#ifdef INCLUDE_GTK_SUPPORT - load_plugin_symbol(module, "chrysalide_plugin_on_panel_docking", - &class->notify_docking); -#endif - break; - - default: - assert(false); - break; - - } - - break; - - default: - assert(false); - break; + const char *result; /* Valeur finale à renvoyer */ - } + result = plugin->name; - break; - - case DPC_BINARY_PROCESSING: - - switch (sub) - { - case DPS_CONTENT: - - switch (action) - { - case PGA_CONTENT_EXPLORER: - case PGA_CONTENT_RESOLVER: - load_plugin_symbol(module, "chrysalide_plugin_handle_binary_content", - &class->handle_content); - break; - - case PGA_CONTENT_ANALYZED: - load_plugin_symbol(module, "chrysalide_plugin_handle_loaded_content", - &class->handle_loaded); - break; - - default: - assert(false); - break; - - } - - break; - - case DPS_FORMAT: - - switch (action) - { - case PGA_FORMAT_ANALYSIS_STARTED: - case PGA_FORMAT_ANALYSIS_ENDED: - case PGA_FORMAT_POST_ANALYSIS_STARTED: - case PGA_FORMAT_POST_ANALYSIS_ENDED: - load_plugin_symbol(module, "chrysalide_plugin_handle_binary_format_analysis", - &class->handle_fmt_analysis); - break; - - case PGA_FORMAT_PRELOAD: - load_plugin_symbol(module, "chrysalide_plugin_preload_binary_format", &class->preload_format); - break; - - case PGA_FORMAT_ATTACH_DEBUG: - load_plugin_symbol(module, "chrysalide_plugin_attach_debug", &class->attach_debug); - break; + return result; - default: - assert(false); - break; +} - } - break; +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Fournit une description fonctionnelle d'un greffon. * +* * +* Retour : Description textuelle associée à une extension ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ - case DPS_DISASSEMBLY: - load_plugin_symbol(module, "chrysalide_plugin_process_disassembly_event", &class->process_disass); - break; +const char *g_plugin_module_get_desc(const GPluginModule *plugin) +{ + const char *result; /* Valeur finale à renvoyer */ - case DPS_DETECTION: - load_plugin_symbol(module, "chrysalide_plugin_detect_external_tools", &class->detect); - break; + result = plugin->desc; - default: - assert(false); - break; + return result; - } +} -#endif - break; +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Fournit la version d'un greffon et de ses fonctionnalités. * +* * +* Retour : Version sous forme de chaîne de caractères ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ - default: - assert(false); - break; +const char *g_plugin_module_get_version(const GPluginModule *plugin) +{ + const char *result; /* Valeur finale à renvoyer */ - } + result = plugin->version; - } + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à valider. * +* Paramètres : plugin = greffon à consulter. * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Fournit l'URL des ressources en ligne liées à un greffon. * * * -* Retour : Désignation brute du greffon. * +* Retour : URL de renvoi associée à une extension ou NULL. * * * * Remarques : - * * * ******************************************************************************/ -char *g_plugin_module_get_modname(const GPluginModule *plugin) +const char *g_plugin_module_get_url(const GPluginModule *plugin) { - char *result; /* Désignation brute à renvoyer*/ - GPluginModuleClass *class; /* Classe de l'instance active */ - - class = G_PLUGIN_MODULE_GET_CLASS(plugin); - - result = class->get_modname(plugin); + const char *result; /* Valeur finale à renvoyer */ - /** - * Tente une opération de la dernière chance. - * - * Dans le cas d'un module Python, la fonction de classe peut ne pas - * trouver de support si l'extension Python n'est pas au point. - */ - if (result == NULL && class->get_modname != _g_plugin_module_get_modname) - result = _g_plugin_module_get_modname(plugin); + result = plugin->url; return result; @@ -844,41 +399,24 @@ char *g_plugin_module_get_modname(const GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : plugin = greffon à valider. * +* Paramètres : plugin = greffon à consulter. * +* count = taille de la liste fournie. [OUT] * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Fournit la liste des dépendances d'un greffon donné. * * * -* Retour : Désignation brute du greffon. * +* Retour : Liste des noms d'extensions requises pour une extension. * * * * Remarques : - * * * ******************************************************************************/ -static char *_g_plugin_module_get_modname(const GPluginModule *plugin) +const char * const *g_plugin_module_get_requirements(const GPluginModule *plugin, size_t *count) { - char *result; /* Désignation brute à renvoyer*/ - char *path; /* Chemin à traiter */ - char *filename; /* Nom de bibliothèque partagée*/ - size_t length; /* Taille du nom */ - - path = strdup(g_plugin_module_get_filename(G_PLUGIN_MODULE(plugin))); - - filename = basename(path); - - if (strncmp(filename, "lib", 3) == 0) - filename += 3; - - length = strlen(filename); - - if (length >= 3) - { - if (strncmp(&filename[length - 3], ".so", 3) == 0) - filename[length - 3] = '\0'; - } + const char * const *result; /* Valeur finale à renvoyer */ - result = strdup(filename); + result = CONST_ARRAY_CAST(plugin->required, char); - free(path); + *count = plugin->required_count; return result; @@ -889,7 +427,7 @@ static char *_g_plugin_module_get_modname(const GPluginModule *plugin) * * * Paramètres : plugin = greffon à consulter. * * * -* Description : Indique le fichier contenant le greffon manipulé. * +* Description : Pointe le fichier contenant le greffon manipulé. * * * * Retour : Chemin d'accès au greffon. * * * @@ -897,28 +435,42 @@ static char *_g_plugin_module_get_modname(const GPluginModule *plugin) * * ******************************************************************************/ -const char *g_plugin_module_get_filename(const GPluginModule *plugin) +char *g_plugin_module_get_filename(const GPluginModule *plugin) { - return plugin->filename; + char *result; /* Chemin d'accès à renvoyer */ + GPluginModuleClass *class; /* Classe de l'instance active */ + + class = G_PLUGIN_MODULE_GET_CLASS(plugin); + + result = class->get_filename(plugin); + + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à consulter. * +* Paramètres : plugin = greffon à valider. * * * -* Description : Fournit la description du greffon dans son intégralité. * +* Description : Fournit le nom brut associé au greffon. * * * -* Retour : Interfaçage renseigné. * +* Retour : Désignation brute du greffon. * * * * Remarques : - * * * ******************************************************************************/ -const plugin_interface *g_plugin_module_get_interface(const GPluginModule *plugin) +char *g_plugin_module_get_modname(const GPluginModule *plugin) { - return plugin->interface; + char *result; /* Désignation brute à renvoyer*/ + GPluginModuleClass *class; /* Classe de l'instance active */ + + class = G_PLUGIN_MODULE_GET_CLASS(plugin); + + result = class->get_modname(plugin); + + return result; } @@ -979,7 +531,6 @@ void g_plugin_module_override_flags(GPluginModule *plugin, PluginStatusFlags fla bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule **list, size_t count) { bool result; /* Bilan à faire remonter */ - const plugin_interface *pg_iface; /* Définition du greffon */ bitfield_t *new; /* Nouvelle définition */ size_t i; /* Boucle de parcours */ GPluginModule *dependency; /* Module nécessaire */ @@ -997,15 +548,13 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule * if ((plugin->flags & (PSF_UNKNOW_DEP | PSF_DEP_LOOP)) == 0) { - pg_iface = g_plugin_module_get_interface(plugin); - /* Collecte des dépendances */ new = dup_bit_field(plugin->dependencies); - for (i = 0; i < pg_iface->required_count; i++) + for (i = 0; i < plugin->required_count; i++) { - dependency = get_plugin_by_name(pg_iface->required[i], &index); + dependency = get_plugin_by_name(plugin->required[i], &index); if (dependency == NULL) plugin->flags |= PSF_UNKNOW_DEP; @@ -1015,7 +564,7 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule * if (dependency->dependencies == NULL) dependency->dependencies = create_bit_field(count, false); - set_in_bit_field(new, index, 1); + set_in_bit_field(new, index); or_bit_field(new, dependency->dependencies); /** @@ -1023,7 +572,7 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule * */ if (test_in_bit_field(plugin->dependencies, index)) - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); } @@ -1041,13 +590,14 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule * /* Vérification sanitaire */ - dependency = get_plugin_by_name(pg_iface->name, &index); + dependency = get_plugin_by_name(plugin->name, &index); assert(dependency != NULL); + assert(dependency == plugin); if (test_in_bit_field(plugin->dependencies, index)) plugin->flags |= PSF_DEP_LOOP; - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); } @@ -1075,12 +625,12 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co { bool result; /* Bilan à retourner */ PluginStatusFlags flags; /* Fanions de greffon */ - const plugin_interface *pg_iface; /* Définition du greffon */ size_t i; /* Boucle de parcours */ GPluginModule *dependency; /* Module nécessaire */ + char *filename; /* Chemin d'accès au greffon */ GPluginModuleClass *class; /* Classe de l'instance active */ - //GGenConfig *config; /* Configuration à charger */ - char *dir; /* Répertoire modifiable */ + char *tmp; /* Chaîne modifiable */ + char *dir; /* Pointeur vers répertoire */ /* Si un essai précédent a déjà échoué ou réussi... */ @@ -1092,40 +642,41 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co /* Chargement des dépendances */ - pg_iface = g_plugin_module_get_interface(plugin); - result = true; - for (i = 0; i < pg_iface->required_count && result; i++) + filename = g_plugin_module_get_filename(plugin); + + for (i = 0; i < plugin->required_count && result; i++) { - dependency = get_plugin_by_name(pg_iface->required[i], NULL); + dependency = get_plugin_by_name(plugin->required[i], NULL); assert(dependency != NULL); result = g_plugin_module_load(dependency, list, count); - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); } if (!result) { - log_variadic_message(LMT_ERROR, - _("Some dependencies failed to load for plugin '%s'"), plugin->filename); + log_variadic_message(LMT_ERROR, _("Some dependencies failed to load for plugin '%s'"), filename); + + plugin->flags |= PSF_FAILURE; goto failure; + } /* Chargement du greffon courant */ class = G_PLUGIN_MODULE_GET_CLASS(plugin); - if (class->init != NULL) + if (class->enable != NULL) { - result = class->init(plugin); + result = class->enable(plugin); if (!result) { - log_variadic_message(LMT_ERROR, - _("Plugin '%s' failed to load itself..."), plugin->filename); + log_variadic_message(LMT_ERROR, _("Plugin '%s' failed to load itself..."), filename); plugin->flags |= PSF_FAILURE; goto failure; @@ -1134,39 +685,23 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co } - g_plugin_module_create_config(plugin); - - result = g_plugin_module_manage(plugin, PGA_PLUGIN_LOADED); - - if (!result) - { - log_variadic_message(LMT_ERROR, - _("Plugin '%s' failed to complete loading..."), plugin->filename); - - plugin->flags |= PSF_FAILURE; - goto failure; - - } - - /* - config = g_plugin_module_get_config(plugin); - g_generic_config_read(config); - g_object_unref(G_OBJECT(config)); - */ + /* Message de bilan */ - dir = strdup(plugin->filename); - dir = dirname(dir); + tmp = strdup(filename); + dir = dirname(tmp); log_variadic_message(LMT_PROCESS, _("Loaded the '<b>%s</b>' file as plugin from the '<b>%s</b>' directory"), - strrchr(plugin->filename, G_DIR_SEPARATOR) + 1, dir); + strrchr(filename, G_DIR_SEPARATOR) + 1, dir); - free(dir); + free(tmp); plugin->flags |= PSF_LOADED; failure: + free(filename); + return result; } @@ -1203,7 +738,7 @@ char *g_plugin_module_build_config_filename(const GPluginModule *plugin, const c suffix = stradd(suffix, G_DIR_SEPARATOR_S); suffix = stradd(suffix, final); - result = get_xdg_config_dir(suffix); + result = get_xdg_config_dir(suffix, true); free(suffix); free(modname); @@ -1227,31 +762,6 @@ char *g_plugin_module_build_config_filename(const GPluginModule *plugin, const c /****************************************************************************** * * -* Paramètres : plugin = greffon à compléter. * -* * -* Description : Met en place la configuration dédiée au greffon. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_plugin_module_create_config(GPluginModule *plugin) -{ - char *filename; /* Chemin d'accès particulier */ - - filename = g_plugin_module_build_config_filename(plugin, "config.xml", false); - - //plugin->config = g_generic_config_new_from_file(filename); - - free(filename); - -} - - -/****************************************************************************** -* * * Paramètres : plugin = greffon à consulter. * * * * Description : Fournit la configuration mise en place pour le greffon. * @@ -1295,11 +805,11 @@ void g_plugin_module_log_simple_message(const GPluginModule *plugin, LogMessageT size_t len; /* Taille tampon disponible */ char *buffer; /* Tampon du msg reconstitué */ - len = 4 + strlen(plugin->interface->name) + 6 + strlen(msg) + 1; + len = 4 + strlen(plugin->name) + 6 + strlen(msg) + 1; buffer = calloc(len, sizeof(char)); strcpy(buffer, "<i>["); - strcat(buffer, plugin->interface->name); + strcat(buffer, plugin->name); strcat(buffer, "]</i> "); strcat(buffer, msg); @@ -1345,80 +855,12 @@ void g_plugin_module_log_variadic_message(const GPluginModule *plugin, LogMessag } -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* * -* Description : Encadre une étape de la vie d'un greffon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_plugin_module_manage(GPluginModule *plugin, PluginAction action) -{ - bool result; /* Bilan à faire remonter */ - GPluginModuleClass *class; /* Classe de l'instance active */ - const plugin_interface *pg_iface; /* Informations à consulter */ - size_t i; /* Boucle de parcours */ - bool handle_action; /* Action supportée ? */ - - class = G_PLUGIN_MODULE_GET_CLASS(plugin); - - if (class->manage == NULL) - result = true; - - else - { - handle_action = false; - - pg_iface = g_plugin_module_get_interface(plugin); - - for (i = 0; i < pg_iface->actions_count; i++) - if (pg_iface->actions[i] == PGA_PLUGIN_LOADED) - { - handle_action = true; - break; - } - - if (handle_action) - result = class->manage(plugin/*, action*/); - else - result = true; - - } - - return result; - -} - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* unused = variable non utilisé pour l'usage de __VA_ARGS__. * -* * -* Description : Accompagne la fin du chargement des modules. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ -void g_plugin_module_notify_plugins_loaded(GPluginModule *plugin, PluginAction action, void *unused) -{ - GPluginModuleClass *class; /* Classe de l'instance active */ - class = G_PLUGIN_MODULE_GET_CLASS(plugin); - class->plugins_loaded(plugin, action); -} +#if 0 /****************************************************************************** @@ -1450,8 +892,6 @@ gpointer g_plugin_module_build_type_instance(GPluginModule *plugin, PluginAction -#if 0 - #ifdef INCLUDE_GTK_SUPPORT diff --git a/src/plugins/plugin.h b/src/plugins/plugin.h index 5c473b2..a4cc388 100644 --- a/src/plugins/plugin.h +++ b/src/plugins/plugin.h @@ -52,6 +52,27 @@ DECLARE_GTYPE(GPluginModule, g_plugin_module, G, PLUGIN_MODULE); +/* Indique le nom associé à un greffon. */ +const char *g_plugin_module_get_name(const GPluginModule *); + +/* Fournit une description fonctionnelle d'un greffon. */ +const char *g_plugin_module_get_desc(const GPluginModule *); + +/* Fournit la version d'un greffon et de ses fonctionnalités. */ +const char *g_plugin_module_get_version(const GPluginModule *); + +/* Fournit l'URL des ressources en ligne liées à un greffon. */ +const char *g_plugin_module_get_url(const GPluginModule *); + +/* Fournit la liste des dépendances d'un greffon donné. */ +const char * const *g_plugin_module_get_requirements(const GPluginModule *, size_t *); + +/* Pointe le fichier contenant le greffon manipulé. */ +char *g_plugin_module_get_filename(const GPluginModule *); + +/* Fournit le nom brut associé au greffon. */ +char *g_plugin_module_get_modname(const GPluginModule *); + /* Fanions indiquant le statut du greffon */ typedef enum _PluginStatusFlags { @@ -66,19 +87,6 @@ typedef enum _PluginStatusFlags #define BROKEN_PLUGIN_STATUS (PSF_UNKNOW_DEP | PSF_DEP_LOOP | PSF_FAILURE) - -/* Crée un module pour un greffon donné. */ -GPluginModule *g_plugin_module_new(const gchar *); - -/* Fournit le nom brut associé au greffon. */ -char *g_plugin_module_get_modname(const GPluginModule *); - -/* Indique le fichier contenant le greffon manipulé. */ -const char *g_plugin_module_get_filename(const GPluginModule *); - -/* Fournit la description du greffon dans son intégralité. */ -const plugin_interface *g_plugin_module_get_interface(const GPluginModule *); - /* Fournit des indications sur l'état du greffon. */ PluginStatusFlags g_plugin_module_get_flags(const GPluginModule *); @@ -103,17 +111,12 @@ void g_plugin_module_log_simple_message(const GPluginModule *, LogMessageType, c /* Présente dans le journal un message complexe. */ void g_plugin_module_log_variadic_message(const GPluginModule *, LogMessageType, const char *, ...); -/* Encadre une étape de la vie d'un greffon. */ -bool g_plugin_module_manage(GPluginModule *, PluginAction); -/* Accompagne la fin du chargement des modules natifs. */ -void g_plugin_module_notify_plugins_loaded(GPluginModule *, PluginAction, void *); +#if 0 /* Crée une instance à partir d'un type dynamique externe. */ gpointer g_plugin_module_build_type_instance(GPluginModule *, PluginAction, GType); -#if 0 - #ifdef INCLUDE_GTK_SUPPORT /* Complète une liste de resources pour thème. */ diff --git a/src/plugins/self.h b/src/plugins/self.h index 4d5ddb0..78a3fe6 100644 --- a/src/plugins/self.h +++ b/src/plugins/self.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * self.h - définitions pour inclusion dans les différents greffons + * self.h - définitions de fonctionnalités facilitant la mise en place d'extensions natives * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,72 +26,55 @@ #define _PLUGINS_SELF_H -#ifndef _PLUGINS_PLUGIN_H -# include "plugin.h" -#endif +#include <assert.h> +#include <malloc.h> + + +#include "plugin.h" #include "../common/compiler.h" -/* Facilitations de déclarations */ +/* Symboles principaux */ + +#define PLUGIN_CORE_SELF \ +static GPluginModule *_this_plugin = NULL; \ +static void chrysalide_plugin_set_self(GPluginModule *); \ +static void chrysalide_plugin_set_self(GPluginModule *plugin) \ +{ \ + assert(_this_plugin == NULL); \ + _this_plugin = plugin; \ +}; \ +__private GPluginModule *_chrysalide_plugin_get_self(void); \ +__private GPluginModule *_chrysalide_plugin_get_self(void) \ +{ \ + return _this_plugin; \ +}; + +#define NATIVE_PLUGIN_ENTRYPOINT(fc) \ +PLUGIN_CORE_SELF; \ +G_MODULE_EXPORT GPluginModule *get_chrysalide_plugin_instance(GModule *); \ +G_MODULE_EXPORT GPluginModule *get_chrysalide_plugin_instance(GModule *module) \ +{ \ + GPluginModule *result; /* Instance à retourner */ \ + result = fc(module); \ + chrysalide_plugin_set_self(result); \ + return result; \ +} + + +/* Spécifications */ #define CHRYSALIDE_WEBSITE(p) "https://www.chrysalide.re/" p -#define EMPTY_PG_LIST(name) \ - name = NULL, \ - name ## _count = 0 \ - -#define BUILD_PG_LIST(name, lst) \ - name = lst, \ - name ## _count = sizeof(lst) / sizeof(lst[0]) \ - -#define AL(...) BUILD_PG_LIST(.actions, ((plugin_action_t []){ __VA_ARGS__ })) - -#define RL(...) BUILD_PG_LIST(.required, ((char *[]){ __VA_ARGS__ })) - -#define NO_REQ EMPTY_PG_LIST(.required) - - -/* Composants d'interface */ - -#define PLUGIN_CORE_SELF \ -static GPluginModule *_this_plugin = NULL; \ -G_MODULE_EXPORT void chrysalide_plugin_set_self(GPluginModule *p); \ -G_MODULE_EXPORT void chrysalide_plugin_set_self(GPluginModule *p) { _this_plugin = p; }; \ -__private GPluginModule *_chrysalide_plugin_get_self(void); \ -__private GPluginModule *_chrysalide_plugin_get_self(void) { return _this_plugin; }; - -#define PLUGIN_CORE_PROPS(n, d, v, u, c) \ - \ - .magic = CHRYSALIDE_PLUGIN_MAGIC, \ - .abi_version = CURRENT_ABI_VERSION, \ - \ - .gtp_name = "G" n "Plugin", \ - .name = n, \ - .desc = d, \ - .version = v, \ - .url = u, \ - \ - .container = c - -#define DEFINE_CHRYSALIDE_PLUGIN(n, d, v, u, r, a) \ -PLUGIN_CORE_SELF \ -G_MODULE_EXPORT const plugin_interface _chrysalide_plugin = { \ - PLUGIN_CORE_PROPS(n, d, v, u, false), \ - r, \ - a, \ -} +#define NO_REQ NULL, 0 -#define DEFINE_CHRYSALIDE_CONTAINER_PLUGIN(n, d, v, u, r, a) \ -PLUGIN_CORE_SELF \ -G_MODULE_EXPORT const plugin_interface _chrysalide_plugin = { \ - PLUGIN_CORE_PROPS(n, d, v, u, true), \ - r, \ - a, \ -} +#define BUILD_PG_LIST(lst) lst, sizeof(lst) / sizeof(lst[0]) + +#define REQ_LIST(...) BUILD_PG_LIST((const char *[]){ __VA_ARGS__ }) -/* Manipulations accélérées */ +/* Journalisation */ __private GPluginModule *_chrysalide_plugin_get_self(void); diff --git a/src/plugins/tweakable-int.h b/src/plugins/tweakable-int.h new file mode 100644 index 0000000..776626f --- /dev/null +++ b/src/plugins/tweakable-int.h @@ -0,0 +1,50 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweakable-int.h - définitions internes propres aux participations aux mécanismes de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_TWEAKABLE_INT_H +#define _PLUGINS_TWEAKABLE_INT_H + + +#include "tweakable.h" + + + +/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */ + + +/* Fournit une liste de sections de configuration. */ +typedef tweak_info_t * (* get_tweakable_plugin_info) (const GTweakablePlugin *, size_t *); + + +/* Greffon avec des compléments pour l'interface de configuration (interface) */ +struct _GTweakablePluginInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + get_tweakable_plugin_info get_info; /* Récupération de section(s) */ + +}; + + + +#endif /* _PLUGINS_TWEAKABLE_INT_H */ diff --git a/src/plugins/tweakable.c b/src/plugins/tweakable.c new file mode 100644 index 0000000..517c4a3 --- /dev/null +++ b/src/plugins/tweakable.c @@ -0,0 +1,98 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweakable.c - participation aux mécanismes de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "tweakable.h" + + +#include "tweakable-int.h" + + + +/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */ + + +/* Procède à l'initialisation de l'interface d'intervention. */ +static void g_tweakable_plugin_default_init(GTweakablePluginInterface *); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTEGRATION DANS L'EDITION DES PREFERENCES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type d'une interface pour l'intervention dans la gestion des greffons. */ +G_DEFINE_INTERFACE(GTweakablePlugin, g_tweakable_plugin, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'intervention. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_tweakable_plugin_default_init(GTweakablePluginInterface *iface) +{ + iface->get_info = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = interface à manipuler. * +* count = taille de la liste renvoyée. [OUT] * +* * +* Description : Fournit une liste de sections de configuration. * +* * +* Retour : Définition(s) de section de configuration ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +tweak_info_t *g_tweakable_plugin_get_tweak_info(const GTweakablePlugin *plugin, size_t *count) +{ + tweak_info_t *result; /* Liste à renvoyer */ + GTweakablePluginInterface *iface; /* Interface utilisée */ + + iface = G_TWEAKABLE_PLUGIN_GET_IFACE(plugin); + + if (iface->get_info != NULL) + result = iface->get_info(plugin, count); + + else + { + *count = 0; + result = NULL; + } + + return result; + +} diff --git a/src/plugins/tweakable.h b/src/plugins/tweakable.h new file mode 100644 index 0000000..aea70b4 --- /dev/null +++ b/src/plugins/tweakable.h @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweakable.h - prototypes pour la participation aux mécanismes de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_TWEAKABLE_H +#define _PLUGINS_TWEAKABLE_H + + +#include "../glibext/helpers.h" +#include "../gtkext/tweak.h" + + + +/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */ + + +#define G_TYPE_TWEAKABLE_PLUGIN (g_tweakable_plugin_get_type()) + +DECLARE_INTERFACE(GTweakablePlugin, g_tweakable_plugin, G, TWEAKABLE_PLUGIN); + + +/* Fournit une liste de sections de configuration. */ +tweak_info_t *g_tweakable_plugin_get_tweak_info(const GTweakablePlugin *, size_t *); + + + +/* -------------------- SOLLICITATION DES FONCTIONNALITES CREEES -------------------- */ + + +#define get_tweakable_plugins_info(c) \ + ({ \ + tweak_info_t *__all_info; \ + __all_info = accumulate_from_all_plugins(G_TYPE_TWEAKABLE_PLUGIN, \ + G_TWEAKABLE_PLUGIN, \ + g_tweakable_plugin_get_tweak_info, \ + tweak_info_t, \ + c); \ + __all_info; \ + }) + + + +#endif /* _PLUGINS_TWEAKABLE_H */ @@ -41,7 +41,9 @@ #include "analysis/scan/scanner.h" #include "analysis/scan/patterns/backends/acism.h" #include "analysis/scan/patterns/backends/bitap.h" -#include "analysis/scan/patterns/backends/hyperscan.h" +#ifdef INCLUDE_HS_SUPPORT +# include "analysis/scan/patterns/backends/hyperscan.h" +#endif #include "core/core.h" #include "core/global.h" #include "core/logs.h" @@ -93,7 +95,11 @@ static void show_rost_help(const char *name) printf("\n"); +#ifdef INCLUDE_HS_SUPPORT printf("\t-A --algorithm=NAME\tSelect one of the available algorithms for data: acism, bitmap, hyperscan (default: acsim).\n"); +#else + printf("\t-A --algorithm=NAME\tSelect one of the available algorithms for data: acism, bitmap (default: acsim).\n"); +#endif printf("\t-C --check-only\t\tValidate the rule syntax without performing a scan (discard the file/dir argument).\n"); printf("\t-j --print-json\t\tPrint matching strings in JSON format instead of simple text.\n"); printf("\t-s --print-strings\tPrint matching strings (default text format only).\n"); @@ -303,8 +309,10 @@ int main(int argc, char **argv) g_scan_options_set_backend_for_data(options, G_TYPE_ACISM_BACKEND); else if (strcmp(optarg, "bitmap") == 0) g_scan_options_set_backend_for_data(options, G_TYPE_BITAP_BACKEND); +#ifdef INCLUDE_HS_SUPPORT else if (strcmp(optarg, "hyperscan") == 0) g_scan_options_set_backend_for_data(options, G_TYPE_HYPERSCAN_BACKEND); +#endif else g_scan_options_set_backend_for_data(options, G_TYPE_INVALID); break; diff --git a/src/schemas/re.chrysalide.framework.gschema.xml b/src/schemas/re.chrysalide.framework.gschema.xml index 4a16f12..534f735 100644 --- a/src/schemas/re.chrysalide.framework.gschema.xml +++ b/src/schemas/re.chrysalide.framework.gschema.xml @@ -1,19 +1,106 @@ <schemalist gettext-domain="chrysalide"> - <schema id="re.chrysalide.framework" path="/re/chrysalide/framework/"> - <child schema="re.chrysalide.framework.mainapp" name="mainapp"/> - </schema> - - <schema id="re.chrysalide.framework.mainapp" path="/re/chrysalide/framework/mainapp/"> - <key name="window-width" type="i"> - <default>600</default> - </key> - <key name="window-height" type="i"> - <default>400</default> - </key> - <key name="window-maximized" type="b"> - <default>false</default> - </key> - </schema> + <schema id="re.chrysalide.framework" path="/re/chrysalide/framework/"> + <child schema="re.chrysalide.framework.paths" name="paths"/> + <child schema="re.chrysalide.framework.gui" name="gui"/> + <child schema="re.chrysalide.framework.tiledgrid" name="tiles"/> + <child schema="re.chrysalide.framework.secstorage" name="secstorage"/> + </schema> + + <schema id="re.chrysalide.framework.paths" path="/re/chrysalide/framework/paths/"> + <key name="tmp-work-dir" type="s"> + <default>"/tmp/chrysalide"</default> + <summary>Directory for temporary contents</summary> + <description> + Location of files created as cache and meant to get deleted when unused + </description> + </key> + </schema> + + <schema id="re.chrysalide.framework.gui" path="/re/chrysalide/framework/gui/"> + <key name="window-width" type="i"> + <default>600</default> + </key> + <key name="window-height" type="i"> + <default>400</default> + </key> + <key name="window-maximized" type="b"> + <default>false</default> + </key> + </schema> + + <flags id="re.chrysalide.framework.tiledgrid.LayoutReachOptions"> + <value nick="left-top-reach" value="1"/> + <value nick="left-bottom-reach" value="2"/> + <value nick="right-top-reach" value="4"/> + <value nick="right-bottom-reach" value="8"/> + </flags> + + <schema id="re.chrysalide.framework.tiledgrid"> + + <key name="layout" flags="re.chrysalide.framework.tiledgrid.LayoutReachOptions"> + <default>["left-top-reach","right-top-reach"]</default> + <summary>Main panel layout</summary> + <description>Layout details describing the border panel locations</description> + </key> + + <key name="top-visibility" type="b"> + <default>false</default> + <summary>Top panel visibility</summary> + <description>Visbility of the panel located at the top of a tiling grid</description> + </key> + + <key name="top-request" type="u"> + <default>300</default> + <summary>Top panel height</summary> + <description>Height request for the panel located at the top of a tiling grid</description> + </key> + + <key name="left-visibility" type="b"> + <default>false</default> + <summary>Left panel visibility</summary> + <description>Visbility of the panel located at the left of a tiling grid</description> + </key> + + <key name="left-request" type="u"> + <default>300</default> + <summary>Left panel height</summary> + <description>Width request for the panel located at the left of a tiling grid</description> + </key> + + <key name="right-visibility" type="b"> + <default>false</default> + <summary>Right panel visibility</summary> + <description>Visbility of the panel located at the right of a tiling grid</description> + </key> + + <key name="right-request" type="u"> + <default>300</default> + <summary>Right panel height</summary> + <description>Width request for the panel located at the right of a tiling grid</description> + </key> + + <key name="bottom-visibility" type="b"> + <default>false</default> + <summary>Bottom panel visibility</summary> + <description>Visbility of the panel located at the bottom of a tiling grid</description> + </key> + + <key name="bottom-request" type="u"> + <default>250</default> + <summary>Bottom panel height</summary> + <description>Height request for the panel located at the bottom of a tiling grid</description> + </key> + + </schema> + + <schema id="re.chrysalide.framework.secstorage" path="/re/chrysalide/framework/secstorage/"> + <key name="salt" type="ay"> + <default>[]</default> + </key> + <key name="master" type="ay"> + <default>[]</default> + </key> + </schema> </schemalist> diff --git a/system/magic/storage b/system/magic/storage new file mode 100644 index 0000000..b7698d1 --- /dev/null +++ b/system/magic/storage @@ -0,0 +1,40 @@ + +# 4.3.7 Local file header: +# Cf. https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + +# local file header signature 4 bytes (0x04034b50) +# version needed to extract 2 bytes +# general purpose bit flag 2 bytes +# compression method 2 bytes +# last mod file time 2 bytes +# last mod file date 2 bytes +# crc-32 4 bytes +# compressed size 4 bytes +# uncompressed size 4 bytes +# file name length 2 bytes +# extra field length 2 bytes + + +# ZIP local file header #1 + +0 string PK\003\004 + +# First entry: control (file name length = 7) + +>0x1a uleshort 7 +>>0x1e string control + +# Content of control + +>>>&(0x1c.h) string COBSTR Chrysalide GObject storage +>>>>&0 string \001\000 (version 1.0) + +# Storage for PythonNotebook + +>>>>>&0 byte 14 +>>>>>>&0 string PythonNotebook - PythonNotebook + +>>>>>>>&0 byte 1 v1 + +!:mime application/vnd.chrysalide.notebook +!:ext cnb diff --git a/tests/analysis/storage/storage.py b/tests/analysis/storage/storage.py deleted file mode 100644 index 612d500..0000000 --- a/tests/analysis/storage/storage.py +++ /dev/null @@ -1,81 +0,0 @@ - -from chrysacase import ChrysalideTestCase -from pychrysalide import core -from pychrysalide.analysis.contents import FileContent -from pychrysalide.analysis.storage import ObjectStorage -from pychrysalide.common import PackedBuffer -import os -import shutil -import tempfile - - -class TestObjectStorage(ChrysalideTestCase): - """TestCase for analysis.storage.""" - - @classmethod - def setUpClass(cls): - - super(TestObjectStorage, cls).setUpClass() - - cls._tmp_path = tempfile.mkdtemp() - - config = core.get_main_configuration() - param = config.search(core.MainParameterKeys.TMPDIR) - - cls._old_tmpdir = param.value - param.value = cls._tmp_path - - cls.log('Using temporary directory "%s"' % cls._tmp_path) - - - @classmethod - def tearDownClass(cls): - - super(TestObjectStorage, cls).tearDownClass() - - config = core.get_main_configuration() - param = config.search(core.MainParameterKeys.TMPDIR) - - param.value = cls._old_tmpdir - - # import os - # os.system('ls -laihR %s' % cls._tmp_path) - - cls.log('Delete directory "%s"' % cls._tmp_path) - - shutil.rmtree(cls._tmp_path) - - - def testFileContentStorage(self): - """Store and load file binary content.""" - - storage = ObjectStorage('my-storage-hash') - self.assertIsNotNone(storage) - - filename = os.path.join(self._tmp_path, 'test.bin') - - with open(filename, 'wb') as fd: - fd.write(b'ABC') - - cnt = FileContent(filename) - self.assertIsNotNone(cnt) - - ret = storage.store_object('contents', cnt) - self.assertEqual(ret, 0) - - pbuf = PackedBuffer() - - ret = storage.store(pbuf) - self.assertTrue(ret) - - self.assertTrue(pbuf.payload_length > 0) - - pbuf.rewind() - - storage2 = ObjectStorage.load(pbuf) - self.assertIsNotNone(storage2) - - cnt2 = storage2.load_object('contents', 0) - self.assertIsNotNone(cnt2) - - self.assertEqual(cnt.data, cnt2.data) diff --git a/tests/arch/immediate.py b/tests/arch/immediate.py deleted file mode 100644 index 74b8069..0000000 --- a/tests/arch/immediate.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - - -import pychrysalide -from chrysacase import ChrysalideTestCase -from pychrysalide import arch -from pychrysalide.arch import ImmOperand - - - -class TestImmediate(ChrysalideTestCase): - """TestCase for arch.ImmOperand.""" - - - def validateValue(self, value, size, padding, strings): - """Check all kinds of things with a given immediate operand.""" - - display = [ - ImmOperand.IOD_BIN, ImmOperand.IOD_OCT, - ImmOperand.IOD_DEC, - ImmOperand.IOD_HEX - ] - - for d in display: - - op = ImmOperand(size, value) - - self.assertTrue(op.size == size) - self.assertTrue(op.value == value) - - op.padding = padding - op.display = d - - string = op.to_string() - self.assertEqual(string, strings[d]) - - - def testByteOne(self): - """Run sanity checks on immediate operand with value 1.""" - - strings = { - ImmOperand.IOD_BIN: 'b1', - ImmOperand.IOD_OCT: '01', - ImmOperand.IOD_DEC: '1', - ImmOperand.IOD_HEX: '0x1' - } - - self.validateValue(1, arch.MDS_8_BITS_UNSIGNED, False, strings) - - - def testByteOnePadded(self): - """Run sanity checks on immediate operand with padded value 1.""" - - strings = { - ImmOperand.IOD_BIN: 'b00000001', - ImmOperand.IOD_OCT: '01', - ImmOperand.IOD_DEC: '1', - ImmOperand.IOD_HEX: '0x01' - } - - self.validateValue(1, arch.MDS_8_BITS_UNSIGNED, True, strings) diff --git a/tests/arch/instruction.py b/tests/arch/instruction.py new file mode 100644 index 0000000..da4d8c1 --- /dev/null +++ b/tests/arch/instruction.py @@ -0,0 +1,52 @@ + +import pychrysalide +from chrysacase import ChrysalideTestCase +from pychrysalide.arch import ArchInstruction + + +class TestProcessor(ChrysalideTestCase): + """TestCase for arch.ArchProcessor.""" + + + def testAbstractClass(self): + """Forbid instruction class instance.""" + + with self.assertRaisesRegex(RuntimeError, 'pychrysalide.arch.ArchInstruction is an abstract class'): + ins = ArchInstruction() + + + def testInstructionBasicImplementation(self): + """Implement basic custom instructions.""" + + + class TodoInstruction(ArchInstruction): + + def __init__(self): + super().__init__(0x123) + + + ins = TodoInstruction() + + with self.assertRaisesRegex(NotImplementedError, 'unexpected NULL value as encoding'): + print(ins.encoding) + + with self.assertRaisesRegex(NotImplementedError, 'unexpected NULL value as keyword'): + print(ins.keyword) + + + class CustomInstruction(ArchInstruction): + + def __init__(self): + super().__init__(0x123) + + def _get_encoding(self): + return 'custom' + + def _get_keyword(self): + return 'kw' + + + ins = CustomInstruction() + + self.assertEqual('custom', ins.encoding) + self.assertEqual('kw', ins.keyword) diff --git a/tests/arch/operand.py b/tests/arch/operand.py new file mode 100644 index 0000000..e8df8b5 --- /dev/null +++ b/tests/arch/operand.py @@ -0,0 +1,72 @@ + +import pychrysalide + +from chrysacase import ChrysalideTestCase +from pychrysalide.arch import ArchOperand +from pychrysalide.glibext import HashableObject, StringBuilder + + +class TestOperand(ChrysalideTestCase): + """TestCase for arch.ArchOperand.""" + + + def testAbstractClass(self): + """Forbid operand class instance.""" + + with self.assertRaisesRegex(RuntimeError, 'pychrysalide.arch.ArchOperand is an abstract class'): + pc = ArchOperand() + + + def testStringBuilderMethodsOverriding(self): + """Override the StringBuilder interface provided by native implementations.""" + + class MyOperand(ArchOperand, StringBuilder): + + def __init__(self): + super().__init__(self) + + def _to_string(self, flags=0): + return 'my-op' + + op = MyOperand() + + self.assertEqual(op.to_string(), 'my-op') + self.assertEqual(str(op), 'my-op') + self.assertEqual(f'{op}', 'my-op') + + + def testHashableObjectMethods(self): + """Test the HashableObject methods implemantation for operands.""" + + # Pas d'implementation de particulière de HashableObject, + # c'est donc la définition d'implémentation d'ArchOperand + # qui s'applique. + + # Spécificité de l'implémentation GLib : hash 32 bits + + class DefaultHashableOperand(ArchOperand): + pass + + def_op = DefaultHashableOperand() + + h = hash(def_op) + + self.assertEqual(0, h & ~0xffffffff) + + + # Définition particulière de l'opérande, sur la base de + # l'implémentation parente. + + class CustomHashableOperand(ArchOperand, HashableObject): + + def _hash(self): + h = self.parent_hash() + h &= ~0xffff + return h + + cust_op = CustomHashableOperand() + + h = hash(cust_op) + + self.assertEqual(0, h & 0xffff) + self.assertEqual(0, h & ~0xffffffff) diff --git a/tests/arch/operands/immediate.py b/tests/arch/operands/immediate.py new file mode 100644 index 0000000..c3fcb84 --- /dev/null +++ b/tests/arch/operands/immediate.py @@ -0,0 +1,86 @@ + +import pychrysalide + +from chrysacase import ChrysalideTestCase +from pychrysalide import MemoryDataSize +from pychrysalide.arch.operands import ImmediateOperand +from pychrysalide.glibext import StringBuilder + + +class TestImmediate(ChrysalideTestCase): + """TestCase for arch.ImmediateOperand.""" + + + def testBasicImmediate(self): + """Check basic properties of immediate values.""" + + imm = ImmediateOperand(MemoryDataSize._32_BITS_UNSIGNED, 0x123) + + self.assertEqual(imm.size, MemoryDataSize._32_BITS_UNSIGNED) + self.assertEqual(imm.value, 0x123) + self.assertFalse(imm.is_negative) + + + def testStringBuilderForImmediatesOverriding(self): + """Override the StringBuilder interface for immediate values.""" + + class MyImmOperand(ImmediateOperand, StringBuilder): + + def __init__(self): + super().__init__(MemoryDataSize._32_BITS_UNSIGNED, 0x123) + + def _to_string(self, flags=0): + return 'NaN' + + op = MyImmOperand() + + self.assertEqual(op.to_string(), 'NaN') + self.assertEqual(str(op), 'NaN') + self.assertEqual(f'{op}', 'NaN') + + + def validateValue(self, value, size, padding, strings): + """Check all kinds of things with a given immediate operand.""" + + for d in strings.keys(): + + op = ImmediateOperand(size, value) + + self.assertTrue(op.size == size) + self.assertTrue(op.value == value) + + if padding: + op.set_flag(ImmediateOperand.ImmOperandFlag.ZERO_PADDING) + else: + op.unset_flag(ImmediateOperand.ImmOperandFlag.ZERO_PADDING_BY_DEFAULT) + + op.display = d + + string = op.to_string() + self.assertEqual(string, strings[d]) + + + def testByteOne(self): + """Run sanity checks on immediate operand with value 1.""" + + strings = { + ImmediateOperand.ImmOperandDisplay.BIN: 'b1', + ImmediateOperand.ImmOperandDisplay.OCT: '01', + ImmediateOperand.ImmOperandDisplay.DEC: '1', + ImmediateOperand.ImmOperandDisplay.HEX: '0x1' + } + + self.validateValue(1, pychrysalide.MemoryDataSize._8_BITS_UNSIGNED, False, strings) + + + def testByteOnePadded(self): + """Run sanity checks on immediate operand with padded value 1.""" + + strings = { + ImmediateOperand.ImmOperandDisplay.BIN: 'b00000001', + ImmediateOperand.ImmOperandDisplay.OCT: '01', + ImmediateOperand.ImmOperandDisplay.DEC: '1', + ImmediateOperand.ImmOperandDisplay.HEX: '0x01' + } + + self.validateValue(1, pychrysalide.MemoryDataSize._8_BITS_UNSIGNED, True, strings) diff --git a/tests/common/bitfield.py b/tests/common/bitfield.py index 75dfb6e..5d535fd 100644 --- a/tests/common/bitfield.py +++ b/tests/common/bitfield.py @@ -93,7 +93,7 @@ class TestBitFields(ChrysalideTestCase): bf_a = BitField(75, 0) bf_b = BitField(4, 1) - bf_b.reset(2, 1) + bf_b.reset(2) bf_a.or_at(bf_b, 63) @@ -130,14 +130,14 @@ class TestBitFields(ChrysalideTestCase): bf_t = BitField(75, 0) for i in range(75): - bf_t.set(i, 1) + bf_t.set(i) self.assertEqual(bf_t, bf_1) self.assertEqual(bf_t.popcount, bf_1.popcount) for i in range(75): - bf_t.reset(i, 1) + bf_t.reset(i) self.assertEqual(bf_t, bf_0) @@ -223,8 +223,8 @@ class TestBitFields(ChrysalideTestCase): """Check bitfield comparison.""" bf_a = BitField(9, 0) - bf_a.set(0, 1) - bf_a.set(5, 1) + bf_a.set(0) + bf_a.set(5) bf_b = BitField(9, 1) @@ -240,7 +240,7 @@ class TestBitFields(ChrysalideTestCase): bits = [ 0, 1, 50, 63, 64, 65, 111 ] for b in bits: - bf.set(b, 1) + bf.set(b) prev = None found = [] @@ -261,17 +261,17 @@ class TestBitFields(ChrysalideTestCase): def testRealCase00(self): - """Test bits in bitfields against other bitfields in a real case (#02).""" + """Test bits in bitfields against other bitfields in a real case (#00).""" bf = BitField(128, 0) for b in [ 0, 50, 54, 58, 66, 70, 98 ]: - bf.set(b, 1) + bf.set(b) mask = BitField(128, 0) for b in [ 0, 51 ]: - mask.set(b, 1) + mask.set(b) self.assertFalse(bf.test_zeros_with(0, mask)) @@ -284,7 +284,7 @@ class TestBitFields(ChrysalideTestCase): self.assertTrue(bf.test_zeros_with(0, mask)) for b in [ 0, 8, 9, 10 ]: - mask.set(b, 1) + mask.set(b) self.assertTrue(bf.test_zeros_with(0, mask)) @@ -305,7 +305,7 @@ class TestBitFields(ChrysalideTestCase): bits = [ 0, 50, 54, 58, 66, 70, 98 ] for b in bits: - mask.set(b, 1) + mask.set(b) bf.or_at(mask, 0) diff --git a/tests/common/leb128.py b/tests/common/leb128.py index db3013e..037af4d 100644 --- a/tests/common/leb128.py +++ b/tests/common/leb128.py @@ -1,7 +1,6 @@ from chrysacase import ChrysalideTestCase from pychrysalide.common import pack_uleb128, unpack_uleb128, pack_leb128, unpack_leb128 -from pychrysalide.common import PackedBuffer class TestLEB128Values(ChrysalideTestCase): @@ -16,34 +15,30 @@ class TestLEB128Values(ChrysalideTestCase): 128: b'\x80\x01', } - for value, encoding in cases.items(): - - pbuf = PackedBuffer() + # Lecture depuis des blocs individuels - status = pack_uleb128(value, pbuf) - self.assertTrue(status) + for value, encoding in cases.items(): - self.assertEqual(pbuf.payload_length, len(encoding)) + self.assertEqual(pack_uleb128(value), encoding) - pbuf.rewind() + v, r = unpack_uleb128(encoding) - got = pbuf.extract(len(encoding)) + self.assertEqual(value, v) + self.assertEqual(b'', r) - self.assertEqual(got, encoding) + # Lecture depuis un bloc commun - self.assertFalse(pbuf.more_data) + data = b''.join(cases.values()) - for value, encoding in cases.items(): + values = [] - pbuf = PackedBuffer() - pbuf.extend(encoding, False) + while len(data) > 0: - pbuf.rewind() + val, data = unpack_uleb128(data) - got = unpack_uleb128(pbuf) - self.assertIsNotNone(got) + values.append(val) - self.assertEqual(got, value) + self.assertEqual(values, [ k for k in cases.keys() ]) def testSignedLeb128Encoding(self): @@ -55,54 +50,39 @@ class TestLEB128Values(ChrysalideTestCase): -9001: b'\xd7\xb9\x7f', } - for value, encoding in cases.items(): + # Lecture depuis des blocs individuels - pbuf = PackedBuffer() + for value, encoding in cases.items(): - status = pack_leb128(value, pbuf) - self.assertTrue(status) + self.assertEqual(pack_leb128(value), encoding) - self.assertEqual(pbuf.payload_length, len(encoding)) + v, r = unpack_leb128(encoding) - pbuf.rewind() + self.assertEqual(value, v) + self.assertEqual(b'', r) - got = pbuf.extract(len(encoding)) + # Lecture depuis un bloc commun - self.assertEqual(got, encoding) + data = b''.join(cases.values()) - self.assertFalse(pbuf.more_data) + values = [] - for value, encoding in cases.items(): + while len(data) > 0: - pbuf = PackedBuffer() - pbuf.extend(encoding, False) + val, data = unpack_leb128(data) - pbuf.rewind() + values.append(val) - got = unpack_leb128(pbuf) - self.assertIsNotNone(got) - - self.assertEqual(got, value) + self.assertEqual(values, [ k for k in cases.keys() ]) def testTooBigLeb128Encodings(self): """Prevent overflow for LEB128 values.""" - pbuf = PackedBuffer() - pbuf.extend(b'\x80' * 10 + b'\x7f', False) - - pbuf.rewind() - - got = unpack_uleb128(pbuf) - - self.assertIsNone(got) - - pbuf = PackedBuffer() - pbuf.extend(b'\x80' * 10 + b'\x7f', False) - - pbuf.rewind() + v = unpack_uleb128(b'\x80' * 10 + b'\x7f') - got = unpack_leb128(pbuf) + self.assertIsNone(v) - self.assertIsNone(got) + v = unpack_leb128(b'\x80' * 10 + b'\x7f') + self.assertIsNone(v) diff --git a/tests/common/xdg.py b/tests/common/xdg.py new file mode 100644 index 0000000..df03c3c --- /dev/null +++ b/tests/common/xdg.py @@ -0,0 +1,71 @@ + +import os + +from chrysacase import ChrysalideTestCase +from pychrysalide.common import get_xdg_cache_dir, get_xdg_config_dir, get_xdg_data_dir, \ + get_xdg_state_dir, get_xdg_runtime_dir + + +class TestXDG(ChrysalideTestCase): + """TestCase for XDG directories.""" + + def testXDGCachePath(self): + """Retrieve the XDG cache directory.""" + + filename = get_xdg_cache_dir('test.txt', False) + + self.assertIsNotNone(filename) + self.assertTrue(filename.startswith(os.sep)) + self.assertTrue(filename.endswith('test.txt')) + + # Depends on current configuration + self.assertTrue('.cache' in filename) + + + def testXDGConfigPath(self): + """Retrieve the XDG config directory.""" + + filename = get_xdg_config_dir('test.txt', False) + + self.assertIsNotNone(filename) + self.assertTrue(filename.startswith(os.sep)) + self.assertTrue(filename.endswith('test.txt')) + + # Depends on current configuration + self.assertTrue('.config' in filename) + + + def testXDGDataPath(self): + """Retrieve the XDG data directory.""" + + filename = get_xdg_data_dir('test.txt', False) + + self.assertIsNotNone(filename) + self.assertTrue(filename.startswith(os.sep)) + self.assertTrue(filename.endswith('test.txt')) + + # Depends on current configuration + self.assertTrue(os.path.join('.local', 'share') in filename) + + + def testXDGStatePath(self): + """Retrieve the XDG state directory.""" + + filename = get_xdg_state_dir('test.txt', False) + + self.assertIsNotNone(filename) + self.assertTrue(filename.startswith(os.sep)) + self.assertTrue(filename.endswith('test.txt')) + + # Depends on current configuration + self.assertTrue(os.path.join('.local', 'state') in filename) + + + def testXDGRuntimePath(self): + """Retrieve the XDG runtime directory.""" + + filename = get_xdg_runtime_dir('test.txt') + + self.assertIsNotNone(filename) + self.assertTrue(filename.startswith(os.sep)) + self.assertTrue(filename.endswith('test.txt')) diff --git a/tests/constval.py b/tests/constval.py deleted file mode 100644 index eafb8d3..0000000 --- a/tests/constval.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - - -from chrysacase import ChrysalideTestCase -from pychrysalide import PyConstvalObject -from pychrysalide.arch import ArchInstruction -import pickle - - -class TestConstVal(ChrysalideTestCase): - """TestCase for PyConstvalObject.""" - - - def testCreation(self): - """Validate PyConstvalObject creation from Python.""" - - cst = PyConstvalObject(123, 'XXX') - - self.assertEqual(cst, 123) - - self.assertEqual(str(cst), 'XXX') - - - def testString(self): - """Validate the PyConstvalObject implementation.""" - - self.assertEqual(ArchInstruction.ILT_JUMP, 1) - - self.assertEqual(str(ArchInstruction.ILT_JUMP), 'ILT_JUMP') - - - def testStorage(self): - """Ensure PyConstvalObject instances are storable.""" - - cst = ArchInstruction.ILT_JUMP - - data = pickle.dumps(cst) - - cst = pickle.loads(data) - - self.assertEqual(cst, ArchInstruction.ILT_JUMP) - - self.assertEqual(str(cst), 'ILT_JUMP') diff --git a/tests/format/executable.py b/tests/format/executable.py new file mode 100644 index 0000000..ec42ccd --- /dev/null +++ b/tests/format/executable.py @@ -0,0 +1,59 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.analysis.contents import MemoryContent +from pychrysalide.arch import vmpa +from pychrysalide.format import ExecutableFormat + + +class TestExecutableFormat(ChrysalideTestCase): + """TestCase for format.ExecutableFormat.""" + + + def testMainAddresses(self): + """Provide several kinds of main addresses.""" + + data = b'\x00\x00\x00\xef' + cnt = MemoryContent(data) + + + class CustomFormatVmpa(ExecutableFormat): + + def _get_main_address(self): + return vmpa(246, 357) + + cf = CustomFormatVmpa(cnt) + + self.assertEqual(cf.main_address.phys, 246) + self.assertEqual(cf.main_address.virt, 357) + + + class CustomFormatInt(ExecutableFormat): + + def _get_main_address(self): + return 123 + + cf = CustomFormatInt(cnt) + + self.assertIsNone(cf.main_address.phys) + self.assertEqual(cf.main_address.virt, 123) + + + class CustomFormatNone(ExecutableFormat): + + def _get_main_address(self): + return None + + cf = CustomFormatNone(cnt) + + self.assertIsNone(cf.main_address) + + + class CustomFormatBad(ExecutableFormat): + + def _get_main_address(self): + return 'bad' + + cf = CustomFormatBad(cnt) + + with self.assertRaisesRegex(Exception, 'unable to define a value for the main address'): + ma = cf.main_address diff --git a/tests/format/flat.py b/tests/format/flat.py index 6924e42..f84f7a4 100644 --- a/tests/format/flat.py +++ b/tests/format/flat.py @@ -1,16 +1,11 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - - -# Tests minimalistes pour valider la gestion des erreurs relevées. - from chrysacase import ChrysalideTestCase -from pychrysalide.analysis import LoadedBinary +from pychrysalide import SourceEndian +#from pychrysalide.analysis import LoadedBinary from pychrysalide.analysis.contents import MemoryContent -from pychrysalide.arch import vmpa +#from pychrysalide.arch import vmpa from pychrysalide.format import FlatFormat -from pychrysalide.glibext import BinPortion +#from pychrysalide.glibext import BinPortion class TestFlatFormat(ChrysalideTestCase): @@ -24,18 +19,21 @@ class TestFlatFormat(ChrysalideTestCase): cnt = MemoryContent(data) - fmt = FlatFormat(cnt) - fmt.set_machine('armv7') + fmt = FlatFormat(cnt, 'armv7', SourceEndian.LITTLE) + + self.assertEqual(fmt.target_machine, 'armv7') + self.assertEqual(fmt.endianness, SourceEndian.LITTLE) + - base = vmpa(0, 0) + # base = vmpa(0, 0) - p = BinPortion(BinPortion.BPC_CODE, base, cnt.size) - p.rights = BinPortion.PAC_READ | BinPortion.PAC_EXEC + # p = BinPortion(BinPortion.BPC_CODE, base, cnt.size) + # p.rights = BinPortion.PAC_READ | BinPortion.PAC_EXEC - fmt.register_user_portion(p) + # fmt.register_user_portion(p) - binary = LoadedBinary(fmt) + # binary = LoadedBinary(fmt) - binary.analyze_and_wait() + # binary.analyze_and_wait() - self.assertTrue(list(binary.processor.instrs)[0].keyword == 'svc') + # self.assertTrue(list(binary.processor.instrs)[0].keyword == 'svc') diff --git a/tests/format/format.py b/tests/format/format.py deleted file mode 100644 index b6aad8f..0000000 --- a/tests/format/format.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - - -# Tests minimalistes pour valider la gestion des erreurs relevées. - - -from chrysacase import ChrysalideTestCase -from pychrysalide.arch import vmpa, mrange -from pychrysalide.format import BinFormat -from pychrysalide.format import BinSymbol -import os -import sys - - -class SimpleFormat(BinFormat): - pass - - -class TestFormatErrors(ChrysalideTestCase): - """TestCase for format.BinFormat.""" - - - def create_fake_symbol(self, index): - saddr = vmpa(index * 0x10, vmpa.VMPA_NO_VIRTUAL) - srange = mrange(saddr, 0x3) - symbol = BinSymbol(BinSymbol.STP_ENTRY_POINT, srange) - return symbol - - - def testBasicSymbolOperations(self): - """Deal with the basic operations related to symbols in a binary format.""" - - sf = SimpleFormat() - - self.assertTrue(len(list(sf.symbols)) == 0) - - symbols = [ self.create_fake_symbol(i) for i in range(4) ] - s0, s1, s2, s3 = symbols - - for s in symbols: - sf.add_symbol(s) - - self.assertTrue(len(list(sf.symbols)) == len(symbols)) - - sf.remove_symbol(s2) - - self.assertTrue(list(sf.symbols) == [s0, s1, s3]) - - - def testBadParamsForAdding(self): - """Check if bad parameters fail for adding a new symbol.""" - - sf = SimpleFormat() - - with self.assertRaises(TypeError): - sf.add_symbol('s') - - - def testWrongRemoval(self): - """Try to remove a wrong symbol from a format.""" - - sf = SimpleFormat() - - s23 = self.create_fake_symbol(23) - sf.remove_symbol(s23) diff --git a/tests/format/known.py b/tests/format/known.py index 056238f..3a51f31 100644 --- a/tests/format/known.py +++ b/tests/format/known.py @@ -1,6 +1,3 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - from chrysacase import ChrysalideTestCase from pychrysalide.analysis.contents import MemoryContent @@ -11,8 +8,62 @@ class TestKnownFormat(ChrysalideTestCase): """TestCase for format.KnownFormat.""" + def testCustomInstance(self): + """Validate a full custom KnownFormat implementation.""" + + data = b'\x01\x02\x03' + cnt = MemoryContent(data) + + + class CustomFormat(KnownFormat): + + def _get_key(self): + return 'tiny' + + def _get_description(self): + return 'Small description' + + cf = CustomFormat(cnt) + + self.assertEqual(cf.key, 'tiny') + self.assertEqual(cf.description, 'Small description') + + + class EmptyCustomFormat(KnownFormat): + pass + + cf = EmptyCustomFormat(cnt) + + # NotImplementedError: method implementation is missing for '_get_key' + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_get_key'"): + k = cf.key + + # NotImplementedError: method implementation is missing for '_get_description' + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_get_description'"): + k = cf.description + + + class BadCustomFormat(KnownFormat): + + def _get_key(self): + return 123 + + def _get_description(self): + return 456 + + cf = BadCustomFormat(cnt) + + # ValueError: unexpected value type for known format key + with self.assertRaisesRegex(ValueError, 'unexpected value type for known format key'): + k = cf.key + + # ValueError: unexpected value type for known format description + with self.assertRaisesRegex(ValueError, 'unexpected value type for known format description'): + k = cf.description + + def testKnownFormatConstructor(self): - """Build Load a simple content for a flat format.""" + """Load a simple content for a known format.""" with self.assertRaisesRegex(RuntimeError, 'pychrysalide.format.KnownFormat is an abstract class'): fmt = KnownFormat() @@ -28,17 +79,3 @@ class TestKnownFormat(ChrysalideTestCase): with self.assertRaisesRegex(TypeError, 'unable to convert the provided argument to binary content'): fmt = MyKnownFormat2(123) - - class MyKnownFormatReady(KnownFormat): - _key = 'rdy' - def __init2__(self, cnt): - super(MyKnownFormatReady, self).__init2__(cnt) - - data = b'\x00\x00\x00\xef' - - cnt = MemoryContent(data) - fmt = MyKnownFormatReady(cnt) - - self.assertIsNotNone(fmt) - - self.assertEqual(fmt.key, 'rdy') diff --git a/tests/format/program.py b/tests/format/program.py new file mode 100644 index 0000000..7027cdf --- /dev/null +++ b/tests/format/program.py @@ -0,0 +1,89 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide import SourceEndian +from pychrysalide.analysis.contents import MemoryContent +#from pychrysalide.arch import vmpa, mrange +from pychrysalide.format import ProgramFormat +#from pychrysalide.format import BinSymbol + + +# class SimpleFormat(BinFormat): +# pass + + +class TestProgramFormat(ChrysalideTestCase): + """TestCase for format.ProgramFormat.""" + + + def testCustomInstance(self): + """Validate a full custom ProgramFormat implementation.""" + + data = b'\x00\x00\x00\xef' + cnt = MemoryContent(data) + + + class CustomFormat(ProgramFormat): + + def _get_endianness(self): + return SourceEndian.BIG + + cf = CustomFormat(cnt) + + self.assertEqual(cf.endianness, SourceEndian.BIG) + + + class EmptyCustomFormat(ProgramFormat): + pass + + cf = EmptyCustomFormat(cnt) + + self.assertEqual(cf.endianness, SourceEndian.LITTLE) + + + + + + + # def create_fake_symbol(self, index): + # saddr = vmpa(index * 0x10, vmpa.VMPA_NO_VIRTUAL) + # srange = mrange(saddr, 0x3) + # symbol = BinSymbol(BinSymbol.STP_ENTRY_POINT, srange) + # return symbol + + + # def testBasicSymbolOperations(self): + # """Deal with the basic operations related to symbols in a binary format.""" + + # sf = SimpleFormat() + + # self.assertTrue(len(list(sf.symbols)) == 0) + + # symbols = [ self.create_fake_symbol(i) for i in range(4) ] + # s0, s1, s2, s3 = symbols + + # for s in symbols: + # sf.add_symbol(s) + + # self.assertTrue(len(list(sf.symbols)) == len(symbols)) + + # sf.remove_symbol(s2) + + # self.assertTrue(list(sf.symbols) == [s0, s1, s3]) + + + # def testBadParamsForAdding(self): + # """Check if bad parameters fail for adding a new symbol.""" + + # sf = SimpleFormat() + + # with self.assertRaises(TypeError): + # sf.add_symbol('s') + + + # def testWrongRemoval(self): + # """Try to remove a wrong symbol from a format.""" + + # sf = SimpleFormat() + + # s23 = self.create_fake_symbol(23) + # sf.remove_symbol(s23) diff --git a/tests/glibext/comparable.py b/tests/glibext/comparable.py new file mode 100644 index 0000000..48291ca --- /dev/null +++ b/tests/glibext/comparable.py @@ -0,0 +1,132 @@ + +import gi + +from chrysacase import ChrysalideTestCase +from gi.repository import GObject +from pychrysalide.glibext import ComparableObject + + +class TestStringBuilder(ChrysalideTestCase): + """Test cases for pychrysalide.glibext.ComparableObject.""" + + + def testComparableObjectCreation(self): + """Create objects with ComparableObject interface.""" + + with self.assertRaisesRegex(NotImplementedError, 'ComparableObject can not be constructed'): + + co = ComparableObject() + + + class NewComparableObjectImplem(gi._gi.GObject, ComparableObject): + pass + + nco = NewComparableObjectImplem() + + self.assertIsNotNone(nco) + + + class NewComparableObjectImplem2(GObject.Object, ComparableObject): + pass + + nco2 = NewComparableObjectImplem() + + self.assertIsNotNone(nco2) + + + def testComparableObjectMethods(self): + """Test the ComparableObject methods.""" + + class BasicComparableObjectImplem(GObject.Object, ComparableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _compare(self, other): + if self._val < other._val: + status = -1 + elif self._val > other._val: + status = 1 + else: + status = 0 + return status + + + a = BasicComparableObjectImplem(123) + b = BasicComparableObjectImplem(456) + + self.assertTrue(a <= b) + + # Sans l'action de inherit_interface_slots(), c'est pygobject_richcompare() qui est appelée, + # laquelle compare simplement les adresses des pointeurs + + c = BasicComparableObjectImplem(789) + d = BasicComparableObjectImplem(234) + + self.assertTrue(c > d) + + + def testComparableObjectExceptions(self): + """Raise exceptions from the ComparableObject interface as expected.""" + + + class OtherComparableObject(GObject.Object, ComparableObject): + pass + + other = OtherComparableObject() + + + class BadComparableObjectImplem(GObject.Object, ComparableObject): + pass + + obj = BadComparableObjectImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_compare'"): + + s = obj < other + + + class BadComparableObjectImplem2(GObject.Object, ComparableObject): + + def _compare(self, other): + return 'AAA' + + obj2 = BadComparableObjectImplem2() + + + with self.assertRaisesRegex(TypeError, 'comparison status has to be a signed integer'): + + s = obj2 < other + + + class BadComparableObjectImplem3a(GObject.Object, ComparableObject): + + def _compare(self, other): + return 123 + + class BadComparableObjectImplem3b(BadComparableObjectImplem3a, ComparableObject): + + def _compare(self, other): + return self.parent_compare() + + obj3 = BadComparableObjectImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + s = obj3 < other + + + class BadComparableObjectImplem4(GObject.Object, ComparableObject): + + def _compare(self, other): + raise Exception('error') + + obj4 = BadComparableObjectImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + s = obj4 < other diff --git a/tests/glibext/configuration.py b/tests/glibext/configuration.py deleted file mode 100644 index 880b445..0000000 --- a/tests/glibext/configuration.py +++ /dev/null @@ -1,106 +0,0 @@ - -import gi -gi.require_version('Gdk', '3.0') -from gi.repository import Gdk - -from chrysacase import ChrysalideTestCase -from pychrysalide.glibext import ConfigParam, GenConfig - - -class TestConfiguration(ChrysalideTestCase): - """TestCase for configuration related items.*""" - - - def testCfgParamValues(self): - """Set and unset configuration parameter values.""" - - color = Gdk.RGBA() - color.parse('#3465A4') - - param = ConfigParam('config.color', ConfigParam.ConfigParamType.COLOR, color) - - self.assertEqual(param.value, color) - - param.make_empty() - - void = Gdk.RGBA(red=0, green=0, blue=0, alpha=0) - self.assertEqual(param.value, void) - - param.value = color - - self.assertEqual(param.value, color) - - - def testCfgParamStates(self): - """Validate all states of an evolving parameter.""" - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER) - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT) - - param.make_empty() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT) - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER, 0x123) - - self.assertEqual(param.value, 0x123) - - self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT) - - param.make_empty() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.CHANGED) - - param.value = 0x1 - - self.assertEqual(param.state, ConfigParam.ConfigParamState.CHANGED) - - param.reset() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT) - - - def testCfgParamDesc(self): - """Export types and states as strings when needed.""" - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER) - - self.assertTrue('|' in str(param.state)) - - self.assertTrue('.' in str(param.type)) - - - def testConfiguration(self): - """Feed and browse a basic configuration.""" - - config = GenConfig() - self.assertIsNotNone(config) - self.assertIsNone(config.filename) - - for i in range(5): - param = ConfigParam('config.int.%u' % i, ConfigParam.ConfigParamType.INTEGER, i) - config.add(param) - - chain = '' - - for p in config.params: - chain += '%u' % p.value - - self.assertTrue(chain == ''.join([ '%u' % i for i in range(5) ])) - - found = config.search('config.int.3') - self.assertTrue(found.value == 3) - - found = config.search('config.int.33') - self.assertIsNone(found) - - for p in config.params: - p.value *= 10 - - chain = '' - - for p in config.params: - chain += '%u' % p.value - - self.assertTrue(chain == ''.join([ '%u' % (i * 10) for i in range(5) ])) diff --git a/tests/glibext/hashable.py b/tests/glibext/hashable.py new file mode 100644 index 0000000..07f22e3 --- /dev/null +++ b/tests/glibext/hashable.py @@ -0,0 +1,190 @@ + +import gi + +from chrysacase import ChrysalideTestCase +from gi.repository import GObject +from pychrysalide.glibext import HashableObject + + +class TestStringBuilder(ChrysalideTestCase): + """Test cases for pychrysalide.glibext.HashableObject.""" + + + def testHashableObjectCreation(self): + """Create objects with HashableObject interface.""" + + with self.assertRaisesRegex(NotImplementedError, 'HashableObject can not be constructed'): + + ho = HashableObject() + + + class NewHashableObjectImplem(gi._gi.GObject, HashableObject): + pass + + nho = NewHashableObjectImplem() + + self.assertIsNotNone(nho) + + + class NewHashableObjectImplem2(GObject.Object, HashableObject): + pass + + nho2 = NewHashableObjectImplem() + + self.assertIsNotNone(nho2) + + + def testHashableObjectMethods(self): + """Test the HashableObject methods.""" + + class BasicHashableObjectImplem(gi._gi.GObject, HashableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _hash(self): + return self._val + + value = 1234 + + ho = BasicHashableObjectImplem(value) + + self.assertEqual(hash(ho), value) + + + class BasicHashableObjectImplem2(GObject.Object, HashableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _hash(self): + return self._val + + value = 5678 + + ho2 = BasicHashableObjectImplem2(value) + + self.assertEqual(hash(ho2), value) + + + def testCascadingHashableObjectImplementations(self): + """Request the hash from the object parent for a full computing.""" + + + class CascadingHashableObjectFullImplemA(GObject.Object, HashableObject): + + def _hash(self): + return 1 + + class CascadingHashableObjectFullImplemB(CascadingHashableObjectFullImplemA, HashableObject): + + def _hash(self): + return super()._hash() * 2 + + class CascadingHashableObjectFullImplemC(CascadingHashableObjectFullImplemB, HashableObject): + + def _hash(self): + return super()._hash() * 3 + + + obj_a = CascadingHashableObjectFullImplemA() + + obj_b = CascadingHashableObjectFullImplemB() + + obj_c = CascadingHashableObjectFullImplemC() + + + self.assertEqual(hash(obj_a), 1) + self.assertEqual(hash(obj_b), 2) + self.assertEqual(hash(obj_c), 6) + + + class CascadingHashableObjectPartialImplemA(GObject.Object, HashableObject): + + def _hash(self): + return 1 + + class CascadingHashableObjectPartialImplemB(CascadingHashableObjectPartialImplemA): + pass + + class CascadingHashableObjectPartialImplemC(CascadingHashableObjectPartialImplemB, HashableObject): + + def _hash(self): + return super()._hash() * 3 + + + obj_a = CascadingHashableObjectPartialImplemA() + + obj_b = CascadingHashableObjectPartialImplemB() + + obj_c = CascadingHashableObjectPartialImplemC() + + + self.assertEqual(hash(obj_a), 1) + self.assertEqual(hash(obj_b), 1) + self.assertEqual(hash(obj_c), 3) + + + def testHashableObjectExceptions(self): + """Raise exceptions from the HashableObject interface as expected.""" + + class BadHashableObjectImplem(GObject.Object, HashableObject): + pass + + obj = BadHashableObjectImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_hash'"): + + h = hash(obj) + + + with self.assertRaisesRegex(TypeError, 'object parent does not implement the HashableObject interface'): + + h = obj.parent_hash() + + + class BadHashableObjectImplem2(GObject.Object, HashableObject): + + def _hash(self): + return 'AAA' + + obj2 = BadHashableObjectImplem2() + + + with self.assertRaisesRegex(TypeError, 'computed hash value has to be an unsigned integer'): + + h = hash(obj2) + + + class BadHashableObjectImplem3a(GObject.Object, HashableObject): + + def _hash(self): + return 123 + + class BadHashableObjectImplem3b(BadHashableObjectImplem3a, HashableObject): + + def _hash(self): + return self.parent_hash() + + obj3 = BadHashableObjectImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + h = hash(obj3) + + + class BadHashableObjectImplem4(GObject.Object, HashableObject): + + def _hash(self): + raise Exception('error') + + obj4 = BadHashableObjectImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + h = hash(obj4) diff --git a/tests/glibext/objhole.py b/tests/glibext/objhole.py new file mode 100644 index 0000000..6a7c2e8 --- /dev/null +++ b/tests/glibext/objhole.py @@ -0,0 +1,31 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.glibext import ThickObject + + +class TestWorks(ChrysalideTestCase): + """TestCase for pychrysalide.glibext.BinaryPortion""" + + def testExtraAccess(self): + """Access to various definitions of the extra data for ThickObject.""" + + obj = ThickObject() + + self.assertEqual(obj.extra, 0) + + obj.extra = 0xffffffe0 + + self.assertEqual(obj.extra, 0xffffffe0) + + obj.extra = 0x00123000 + + self.assertEqual(obj.extra, 0x00123000) + + + def testRservedBits(self): + """Check space leaved as available by the GLib.""" + + obj = ThickObject() + + self.assertTrue(obj._GOBJECT_RESERVED_EXTRA_BITS > 0) + self.assertTrue(obj._GOBJECT_RESERVED_EXTRA_BITS < 32) diff --git a/tests/glibext/portion.py b/tests/glibext/portion.py new file mode 100644 index 0000000..3e91969 --- /dev/null +++ b/tests/glibext/portion.py @@ -0,0 +1,17 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.arch import vmpa +from pychrysalide.glibext import BinaryPortion + + +class TestWorks(ChrysalideTestCase): + """TestCase for pychrysalide.glibext.BinaryPortion""" + + def testBasicBinaryPortion(self): + """Implement a basic binary portion.""" + + addr = vmpa(0, vmpa.VmpaSpecialValue.NO_VIRTUAL) + + p = BinaryPortion(addr, 10) + + self.assertEqual(p.range.addr, addr) diff --git a/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml b/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml new file mode 100644 index 0000000..6afa96b --- /dev/null +++ b/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml @@ -0,0 +1,15 @@ +<schemalist> + + <schema id="re.chrysalide.tests.secstorage" path="/re/chrysalide/tests/secstorage/"> + + <key name="salt" type="ay"> + <default>[]</default> + </key> + + <key name="master" type="ay"> + <default>[]</default> + </key> + + </schema> + +</schemalist> diff --git a/tests/glibext/secstorage.py b/tests/glibext/secstorage.py new file mode 100644 index 0000000..5b8f680 --- /dev/null +++ b/tests/glibext/secstorage.py @@ -0,0 +1,153 @@ + +import gi +import os +import subprocess + +from chrysacase import ChrysalideTestCase +from gi.repository import Gio, GLib +from pychrysalide.glibext import SecretStorage + + +class TestSecretStorage(ChrysalideTestCase): + """TestCase for secret storage features.""" + + @classmethod + def setUpClass(cls): + + super(TestSecretStorage, cls).setUpClass() + + cls.log('Creating GSettings schema...') + + path = os.path.dirname(os.path.realpath(__file__)) + + subprocess.run([ 'glib-compile-schemas', path ]) + + + @classmethod + def tearDownClass(cls): + + super(TestSecretStorage, cls).tearDownClass() + + cls.log('Removing compiled GSettings schema...') + + path = os.path.dirname(os.path.realpath(__file__)) + + filename = os.path.join(path, 'gschemas.compiled') + + if os.path.exists(filename): + os.remove(filename) + + + def _get_settings(self, sid): + """Provide local GSettings instance.""" + + path = os.path.dirname(os.path.realpath(__file__)) + + source = Gio.SettingsSchemaSource.new_from_directory(path, None, True) + + schema = Gio.SettingsSchemaSource.lookup(source, sid, False) + + settings = Gio.Settings.new_full(schema, None, None) + + return settings + + + def testMasterKeyDefinition(self): + """Check for cryptographic parameters for secret storage.""" + + settings = self._get_settings('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('master') + + self.assertEqual(len(settings.get_value('master').unpack()), 0) + + self.assertFalse(storage.has_key) + + settings.set_value('master', GLib.Variant('ay', b'ABC')) + + self.assertFalse(storage.has_key) + + settings.set_value('master', GLib.Variant('ay', b'A' * 23)) + + self.assertTrue(storage.has_key) + + + def testMasterKeyCreation(self): + """Create and update cryptographic parameters for secret storage.""" + + settings = self._get_settings('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('salt') + settings.reset('master') + + self.assertFalse(storage.has_key) + + status = storage.set_password('') + + self.assertTrue(status); + + self.assertTrue(storage.has_key) + self.assertTrue(storage.is_locked) + + status = storage.unlock('') + + self.assertTrue(status) + + self.assertFalse(storage.is_locked) + + storage.lock() + + self.assertTrue(storage.is_locked) + + status = storage.unlock('XXX') + + self.assertFalse(status) + + self.assertTrue(storage.is_locked) + + + def testDataEncryption(self): + """Encrypt and decrypt data with the secret storage.""" + + settings = self._get_settings('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('salt') + settings.reset('master') + + status = storage.set_password('<s3cUre>') + + self.assertTrue(status); + + status = storage.unlock('<s3cUre>') + + self.assertTrue(status) + + + original = b'ABC' + + encrypted = storage.encrypt_data(original) + + self.assertIsNotNone(encrypted) + + plain = storage.decrypt_data(encrypted) + + self.assertIsNotNone(plain) + self.assertEqual(original, plain) + + + original = b'A' * 136 + + encrypted = storage.encrypt_data(original) + + self.assertIsNotNone(encrypted) + + plain = storage.decrypt_data(encrypted) + + self.assertIsNotNone(plain) + self.assertEqual(original, plain) diff --git a/tests/glibext/singleton.py b/tests/glibext/singleton.py index 4588ae5..712aece 100644 --- a/tests/glibext/singleton.py +++ b/tests/glibext/singleton.py @@ -1,21 +1,24 @@ +import gi + from chrysacase import ChrysalideTestCase from gi.repository import GObject -from pychrysalide.glibext import SingletonCandidate, SingletonFactory +from pychrysalide.glibext import ComparableObject, HashableObject, SingletonCandidate, SingletonFactory class TestSingleton(ChrysalideTestCase): """Test cases for pychrysalide.glibext.SingletonFactory.""" - def testSingletonCreation(self): - """Create singleton objects.""" + def testSingletonCandidateCreation(self): + """Create objects with SingletonCandidate interface.""" with self.assertRaisesRegex(NotImplementedError, 'SingletonCandidate can not be constructed'): sc = SingletonCandidate() - class NewSingletonImplem(GObject.Object, SingletonCandidate): + + class NewSingletonImplem(gi._gi.GObject, HashableObject, ComparableObject, SingletonCandidate): pass nsi = NewSingletonImplem() @@ -23,121 +26,132 @@ class TestSingleton(ChrysalideTestCase): self.assertIsNotNone(nsi) - def testFactoryCreation(self): - """Create singleton factories.""" + class NewSingletonImplem2(GObject.Object, HashableObject, ComparableObject, SingletonCandidate): + pass + + nsi2 = NewSingletonImplem2() - sf = SingletonFactory() + self.assertIsNotNone(nsi2) - self.assertIsNotNone(sf) - class MyFactory(SingletonFactory): - pass + # def testFactoryCreation(self): + # """Create singleton factories.""" + + # sf = SingletonFactory() + + # self.assertIsNotNone(sf) + + # class MyFactory(SingletonFactory): + # pass + + # msf = MyFactory() + + # self.assertIsNotNone(msf) + - msf = MyFactory() - self.assertIsNotNone(msf) - def testSingletonMethods(self): - """Test the singleton methods.""" + # def testSingletonMethods(self): + # """Test the singleton methods.""" - class IntegerCacheImplem(GObject.Object, SingletonCandidate): + # class IntegerCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, val): - super().__init__() - self._val = val + # def __init__(self, val): + # super().__init__() + # self._val = val - def _list_inner_instances(self): - return () + # def _list_inner_instances(self): + # return () - def __hash__(self): - return hash('common-key') + # def __hash__(self): + # return hash('common-key') - def __eq__(self, other): - return self._val == other._val + # def __eq__(self, other): + # return self._val == other._val - val_0 = IntegerCacheImplem(0) - val_0_bis = IntegerCacheImplem(0) - val_1 = IntegerCacheImplem(1) + # val_0 = IntegerCacheImplem(0) + # val_0_bis = IntegerCacheImplem(0) + # val_1 = IntegerCacheImplem(1) - self.assertEqual(hash(val_0), hash(val_0_bis)) - self.assertEqual(hash(val_0), hash(val_1)) + # self.assertEqual(hash(val_0), hash(val_0_bis)) + # self.assertEqual(hash(val_0), hash(val_1)) - self.assertEqual(val_0.hash(), val_0_bis.hash()) - self.assertEqual(val_0.hash(), val_1.hash()) + # self.assertEqual(val_0.hash(), val_0_bis.hash()) + # self.assertEqual(val_0.hash(), val_1.hash()) - self.assertTrue(val_0 == val_0_bis) - self.assertFalse(val_0 == val_1) + # self.assertTrue(val_0 == val_0_bis) + # self.assertFalse(val_0 == val_1) - def testSingletonFootprint(self): - """Check for singleton memory footprint.""" + # def testSingletonFootprint(self): + # """Check for singleton memory footprint.""" - sf = SingletonFactory() + # sf = SingletonFactory() - class IntegerCacheImplem(GObject.Object, SingletonCandidate): + # class IntegerCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, val): - super().__init__() - self._val = val + # def __init__(self, val): + # super().__init__() + # self._val = val - def _list_inner_instances(self): - return () + # def _list_inner_instances(self): + # return () - def __hash__(self): - return hash('common-key') + # def __hash__(self): + # return hash('common-key') - def __eq__(self, other): - return self._val == other._val + # def __eq__(self, other): + # return self._val == other._val - def _set_read_only(self): - pass + # def _set_read_only(self): + # pass - val_0 = IntegerCacheImplem(0) - val_0_bis = IntegerCacheImplem(0) - val_1 = IntegerCacheImplem(1) + # val_0 = IntegerCacheImplem(0) + # val_0_bis = IntegerCacheImplem(0) + # val_1 = IntegerCacheImplem(1) - obj = sf.get_instance(val_0) + # obj = sf.get_instance(val_0) - self.assertTrue(obj is val_0) + # self.assertTrue(obj is val_0) - obj = sf.get_instance(val_0_bis) + # obj = sf.get_instance(val_0_bis) - self.assertTrue(obj is val_0) + # self.assertTrue(obj is val_0) - obj = sf.get_instance(val_1) + # obj = sf.get_instance(val_1) - self.assertTrue(obj is val_1) + # self.assertTrue(obj is val_1) - self.assertEqual(len(obj.inner_instances), 0) + # self.assertEqual(len(obj.inner_instances), 0) - class MasterCacheImplem(GObject.Object, SingletonCandidate): + # class MasterCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, children): - super().__init__() - self._children = children + # def __init__(self, children): + # super().__init__() + # self._children = children - def _list_inner_instances(self): - return self._children + # def _list_inner_instances(self): + # return self._children - def _update_inner_instances(self, instances): - self._children = instances + # def _update_inner_instances(self, instances): + # self._children = instances - def __hash__(self): - return hash('master-key') + # def __hash__(self): + # return hash('master-key') - def __eq__(self, other): - return False + # def __eq__(self, other): + # return False - def _set_read_only(self): - pass + # def _set_read_only(self): + # pass - master = MasterCacheImplem(( val_0_bis, val_1 )) + # master = MasterCacheImplem(( val_0_bis, val_1 )) - obj = sf.get_instance(master) + # obj = sf.get_instance(master) - self.assertTrue(obj.inner_instances[0] is val_0) + # self.assertTrue(obj.inner_instances[0] is val_0) - self.assertTrue(obj.inner_instances[1] is val_1) + # self.assertTrue(obj.inner_instances[1] is val_1) diff --git a/tests/glibext/storage.py b/tests/glibext/storage.py new file mode 100644 index 0000000..b60377a --- /dev/null +++ b/tests/glibext/storage.py @@ -0,0 +1,74 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.glibext import ObjectStorage, SerializableObject +import gi +import os +import tempfile + + +class TestObjectStorage(ChrysalideTestCase): + """TestCase for analysis.storage.""" + + @classmethod + def setUpClass(cls): + + super(TestObjectStorage, cls).setUpClass() + + _, cls._tmp_filename = tempfile.mkstemp() + + cls.log('Using temporary filename "%s"' % cls._tmp_filename) + + + @classmethod + def tearDownClass(cls): + + super(TestObjectStorage, cls).tearDownClass() + + cls.log('Delete filename "%s"' % cls._tmp_filename) + + os.unlink(cls._tmp_filename) + + + def testGenericStorage(self): + """Store and load basic objects.""" + + + class SimpleObject(gi._gi.GObject, SerializableObject): + + def __init__(self, b=None): + super().__init__() + self._b = b + + def _load(self, storage, fd): + assert(self._b is None) + self._b = os.read(fd, 1)[0] + return True + + def _store(self, storage, fd): + os.write(fd, bytes([ self._b ])) + return True + + def __eq__(self, other): + return self._b == other._b + + + # Store + + storage = ObjectStorage('TestStorage', 0, 'my-storage-hash') + self.assertIsNotNone(storage) + + so = SimpleObject(0x23) + + pos = storage.store_object('simple', so) + self.assertIsNotNone(pos) + + status = storage.store(self._tmp_filename) + self.assertTrue(status) + + # Reload + + storage2 = ObjectStorage.load(self._tmp_filename) + + so2 = storage2.load_object('simple', pos) + + self.assertEqual(so, so2) diff --git a/tests/glibext/strbuilder.py b/tests/glibext/strbuilder.py new file mode 100644 index 0000000..72fd5c2 --- /dev/null +++ b/tests/glibext/strbuilder.py @@ -0,0 +1,114 @@ + +import gi + +from chrysacase import ChrysalideTestCase +from gi.repository import GObject +from pychrysalide.glibext import StringBuilder + + +class TestStringBuilder(ChrysalideTestCase): + """Test cases for pychrysalide.glibext.StringBuilder.""" + + + def ZZZtestStringBuilderCreation(self): + """Create objects with StringBuilder interface.""" + + with self.assertRaisesRegex(NotImplementedError, 'StringBuilder can not be constructed'): + + sb = StringBuilder() + + + class NewStringBuilderImplem(gi._gi.GObject, StringBuilder): + pass + + nsb = NewStringBuilderImplem() + + self.assertIsNotNone(nsb) + + + class NewStringBuilderImplem2(GObject.Object, StringBuilder): + pass + + nsb2 = NewStringBuilderImplem() + + self.assertIsNotNone(nsb2) + + + def ZZZtestStringBuilderMethods(self): + """Test the StringBuilder methods.""" + + class BasicStringBuilderImplem(GObject.Object, StringBuilder): + + def __init__(self, desc): + super().__init__() + self._desc = desc + + def _to_string(self, flags=0): + return self._desc + + desc = 'simple_desc' + + sb = BasicStringBuilderImplem(desc) + + self.assertEqual(sb.to_string(), desc) + self.assertEqual(str(sb), desc) + self.assertEqual(f'{sb}', desc) + + + def testStringBuilderExceptions(self): + """Raise exceptions from the StringBuilder interface as expected.""" + + + class BadStringBuilderImplem(GObject.Object, StringBuilder): + pass + + obj = BadStringBuilderImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_to_string'"): + + s = str(obj) + + + class BadStringBuilderImplem2(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + return 0xcafe + + obj2 = BadStringBuilderImplem2() + + + with self.assertRaisesRegex(TypeError, 'object description has to get provided as an UTF-8 string value'): + + s = str(obj2) + + + class BadStringBuilderImplem3a(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + return 'desc' + + class BadStringBuilderImplem3b(BadStringBuilderImplem3a, StringBuilder): + + def _to_string(self, flags=0): + return self.parent_to_string() + + obj3 = BadStringBuilderImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + s = str(obj3) + + + class BadStringBuilderImplem4(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + raise Exception('error') + + obj4 = BadStringBuilderImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + s = str(obj4) diff --git a/tests/glibext/work.py b/tests/glibext/work.py new file mode 100644 index 0000000..808e25e --- /dev/null +++ b/tests/glibext/work.py @@ -0,0 +1,26 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.glibext import GenericWork + + +class TestWorks(ChrysalideTestCase): + """TestCase for glibext.GenericWork""" + + def testBasicWorkImplementation(self): + """Implement a basic work.""" + + class BasicWork(GenericWork): + def __init__(self, lst): + super(BasicWork, self).__init__() + self._lst = lst + def _run(self): + self._lst.append('done') + + test = [] + + work = BasicWork(test) + + work.process() + + self.assertEqual(len(test), 1) + self.assertEqual(test[0], 'done') diff --git a/tests/glibext/workqueue.py b/tests/glibext/workqueue.py new file mode 100644 index 0000000..203970b --- /dev/null +++ b/tests/glibext/workqueue.py @@ -0,0 +1,49 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.glibext import GenericWork, WorkQueue +from threading import Lock + + +class TestWorks(ChrysalideTestCase): + """TestCase for glibext.*Work*""" + + def testBasicWorkQueueBehaviour(self): + """Check the default basic behaviour of a work queue.""" + + queue = WorkQueue() + + ret = queue.is_empty(123) + self.assertTrue(ret) + + + def testWorkScheduling(self): + """Check scheduled works results.""" + + class SchedulableWork(GenericWork): + def __init__(self, lck, val): + super(SchedulableWork, self).__init__() + self._lck = lck + self._val = val + def _run(self): + self._lck.acquire() + self._val['integer'] += 1 + self._lck.release() + + lock = Lock() + value = { 'integer': 0 } + + queue = WorkQueue() + + gid = queue.define_group(4) + + count = 31 + + for i in range(count): + + work = SchedulableWork(lock, value) + queue.schedule(work, gid) + + while not(queue.wait_for_completion(gid)): + pass + + self.assertEqual(value['integer'], count) diff --git a/tests/plugins/plugin.py b/tests/plugins/plugin.py index 6409975..9015409 100644 --- a/tests/plugins/plugin.py +++ b/tests/plugins/plugin.py @@ -1,223 +1,96 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - from chrysacase import ChrysalideTestCase -from pychrysalide import PluginModule -import gc +from pychrysalide.plugins import PluginModule class TestPlugin(ChrysalideTestCase): """TestCase for GPluginModule.""" - def testGarbageCollecting(self): - """Ensure the garbarge collector is working for plugin modules.""" - + def testPluginInfoImplementations(self): + """Retrieve plugin basic information provided by __init__().""" - class MyPG_1(PluginModule): + class MyPlugin(PluginModule): def __init__(self): + super().__init__('custom-name', desc='custom-desc', url='custom-url', version='0.0.1') - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } + my = MyPlugin() - super(MyPG_1, self).__init__(**interface) + self.assertEqual(my.name, 'custom-name') + self.assertEqual(my.desc, 'custom-desc') + self.assertEqual(my.version, '0.0.1') + self.assertEqual(my.url, 'custom-url') - pg = MyPG_1() - self.assertIsNotNone(pg) + def testPluginMethodImplementations(self): + """Ensure required plugins abstract methods can be implemented.""" - - class MyPG_2(PluginModule): + class MyWrongPlugin(PluginModule): def __init__(self): + super().__init__('pg-name') - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - - super(MyPG_2, self).__init__(**interface) + my = MyWrongPlugin() + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_get_filename'"): + print(my.filename) - pg = MyPG_2() - self.assertIsNotNone(pg) + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_get_modname'"): + print(my.modname) - class MyPG_3(PluginModule): + class MyPlugin(PluginModule): def __init__(self): + super().__init__('pg-name') - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } + def _get_filename(self): + return 'file-name' - super(MyPG_3, self).__init__(**interface) + def _get_modname(self): + return 'mod-name' + my = MyPlugin() - pg = MyPG_3() - self.assertIsNotNone(pg) + self.assertEqual(my.filename, 'file-name') + self.assertEqual(my.modname, 'mod-name') - class MyPG_4(PluginModule): - - def __init__(self): - - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - super(MyPG_4, self).__init__(**interface) + def testPluginRequirementIncludePython(self): + """Ensure that plugin requirements include the Python bindings.""" - - pg = MyPG_4() - self.assertIsNotNone(pg) - - - class MyPG_5(PluginModule): + class MyPlugin(PluginModule): def __init__(self): + super().__init__('pg-name') - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - - super(MyPG_5, self).__init__(**interface) - - - pg = MyPG_5() - self.assertIsNotNone(pg) + my = MyPlugin() + self.assertEqual(len(my.requirements), 1) + self.assertEqual(my.requirements[0], 'PyChrysalide') - gc.collect() + def testPluginRequirementUniqueness(self): + """Ensure that plugin requirements are unique.""" - def testCreation(self): - """Validate PluginModule creation from Python.""" - - - class MyPG_0(PluginModule): - pass - - - # TypeError: Required argument 'name' (pos 1) not found - with self.assertRaises(TypeError): - pg = MyPG_0() - - - class MyPG_1(PluginModule): + class MyPlugin(PluginModule): def __init__(self): + super().__init__('pg-name', required=[ 'PyChrysalide' ]) - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - - super(MyPG_1, self).__init__(**interface) + my = MyPlugin() + self.assertEqual(my.requirements.count('PyChrysalide'), 1) - pg = MyPG_1() - self.assertIsNotNone(pg) - - class MyPG_2(PluginModule): + class MyPlugin2(PluginModule): def __init__(self): + super().__init__('pg-name2', required=[ 'AA', 'BB', 'AA' ]) - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( 'ABC', ) - } - - super(MyPG_2, self).__init__(**interface) - - - # TypeError: Invalid type for plugin action. - with self.assertRaises(TypeError): - pg = MyPG_2() - - - class MyPG_3(PluginModule): - - def __init__(self): - - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( PluginModule.PGA_CONTENT_EXPLORER, ) - } - - super(MyPG_3, self).__init__(**interface) - - - # TypeError: missing features for the declared actions. - with self.assertRaises(TypeError): - pg = MyPG_3() - - - class MyPG_4(PluginModule): - - def __init__(self): - - interface = { - 'name' : 'some_name4', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( PluginModule.PGA_CONTENT_EXPLORER, ) - } - - super(MyPG_4, self).__init__(**interface) - - def handle_binary_content(self, action, content, wid, status): - pass - - - pg = MyPG_4() - self.assertIsNotNone(pg) - - - def testDoubleUsage(self): - """Validate PluginModule double usage in Python.""" - - - class MyPG(PluginModule): - - def __init__(self): - - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - - super(MyPG, self).__init__(**interface) - - - pg1 = MyPG() - self.assertIsNotNone(pg1) + my = MyPlugin2() - pg2 = MyPG() - self.assertIsNotNone(pg2) + self.assertEqual(my.requirements.count('AA'), 1) + self.assertEqual(my.requirements.count('PyChrysalide'), 1) diff --git a/tests/plugins/python.py b/tests/plugins/python.py new file mode 100644 index 0000000..1a3dd97 --- /dev/null +++ b/tests/plugins/python.py @@ -0,0 +1,27 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.plugins import PythonPlugin + + +class TestPlugin(ChrysalideTestCase): + """TestCase for GPythonPlugin.""" + + + def testPluginInfoImplementations(self): + """Retrieve plugin basic information provided by __init__().""" + + class MyPlugin(PythonPlugin): + """Custom description.""" + + def __init__(self): + super().__init__(__file__, '0.0.1', 'custom-url') + + my = MyPlugin() + + self.assertEqual(my.name, 'MyPlugin') + self.assertEqual(my.desc, 'Custom description.') + self.assertEqual(my.version, '0.0.1') + self.assertEqual(my.url, 'custom-url') + + self.assertEqual(my.filename, __file__) + self.assertEqual(my.modname, 'python') diff --git a/tools/about/build.py b/tools/about/build.py new file mode 100644 index 0000000..2267492 --- /dev/null +++ b/tools/about/build.py @@ -0,0 +1,120 @@ + +# apt install libcairo2-dev pkg-config python3-dev +# pip install pycairo + +import cairo +import math +import sys + +from enum import IntEnum + +from ttf import create_cairo_font_face_for_file + + +WIDTH = 350 +HEIGHT = 430 + + +def deg2rad(degrees): + return degrees * (math.pi / 180) + + +class TextAlignment(IntEnum): + LEFT = 1 + CENTER = 2 + RIGHT = 3 + + +def draw_text(ctx, text, pos, fsize, noise, theta = 0.0, align = TextAlignment.CENTER): + """Dessine un texte en respectant des propriétées.""" + + ctx.save() + + ctx.set_font_size(fsize) + + fascent, fdescent, fheight, fxadvance, fyadvance = ctx.font_extents() + x_off, y_off, tw, th = ctx.text_extents(text)[:4] + + if align == TextAlignment.LEFT: + nx = 0 + elif align == TextAlignment.RIGHT: + nx = -tw + else: + nx = -tw / 2.0 + + ny = fheight / 2 + + ctx.translate(pos[0], pos[1]) + ctx.rotate(theta) + ctx.translate(nx, ny) + ctx.move_to(0,0) + + ctx.text_path(text) + ctx.set_source_surface(noise, 0, -fheight) + ctx.fill_preserve() + ctx.set_source_rgb(1.0, 1.0, 1.0) + ctx.set_line_width(1) + ctx.stroke() + + ctx.restore() + + +if __name__ == '__main__': + """Point d'entrée du script.""" + + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT) + + cr = cairo.Context(surface) + + face = create_cairo_font_face_for_file('cour10p_b.ttf') + cr.set_font_face(face) + + noise = cairo.ImageSurface.create_from_png('noise.png') + + # Type d'édition + + if len(sys.argv) > 2: + + draw_text(cr, sys.argv[2], [ 300, 25 ], 32, noise, + theta = deg2rad(270), align = TextAlignment.RIGHT) + + cr.set_source_rgb(0, 0, 0) + cr.paint_with_alpha(0.4) + + # Numéro de version + + draw_text(cr, sys.argv[1], [ 149, 10 + 259 + 14 + 42 + 2 ], 23, noise, align=TextAlignment.LEFT) + + cr.set_source_rgb(0, 0, 0) + cr.paint_with_alpha(0.5) + + # Logo + + logo = cairo.ImageSurface.create_from_png('logo.png') + + scale_y = 259 / logo.get_height() + + cr.save() + + cr.translate((WIDTH - (logo.get_width() * scale_y)) / 2, 12) + + cr.scale(scale_y, scale_y) + + cr.set_source_surface(logo, 0, 0) + cr.paint() + + cr.restore() + + # Titre + + draw_text(cr, 'Chrysalide', [ WIDTH / 2, 10 + 259 + 14 + 4 ], 42, noise) + + # cr.rectangle(265, 30, 10, 10) + # cr.set_source_rgb(1, 0, 0) + # cr.fill() + + # cr.rectangle(265, 255, 10, 10) + # cr.set_source_rgb(1, 0, 0) + # cr.fill() + + surface.write_to_png('bg.png') diff --git a/tools/about/courier-10-pitch-bold_EQ97V.zip b/tools/about/courier-10-pitch-bold_EQ97V.zip Binary files differnew file mode 100644 index 0000000..0585fe6 --- /dev/null +++ b/tools/about/courier-10-pitch-bold_EQ97V.zip diff --git a/tools/about/courier10point.zip b/tools/about/courier10point.zip Binary files differnew file mode 100644 index 0000000..82cd0de --- /dev/null +++ b/tools/about/courier10point.zip diff --git a/tools/about/gen.sh b/tools/about/gen.sh new file mode 100755 index 0000000..c22ec59 --- /dev/null +++ b/tools/about/gen.sh @@ -0,0 +1,52 @@ +#!/bin/bash + + +# Cf. https://stackoverflow.com/questions/59895/how-do-i-get-the-directory-where-a-bash-script-is-located-from-within-the-script/246128#246128 +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +pushd $SCRIPT_DIR > /dev/null + + +# Obtention de la police Courier-10-Pitch-Bold +# -------------------------------------------- + +# https://fontsgeek.com/fonts/Courier-10-Pitch-Bold +# https://fontsgeek.com/terms-and-conditions + +# => courier-10-pitch-bold_EQ97V.zip + + +# Courier 10 Pitch font missing after upgrade to 17.04? +# https://askubuntu.com/questions/914352/courier-10-pitch-font-missing-after-upgrade-to-17-04 + +# https://groups.google.com/g/trelby/c/CGkpcMBXW9U +# -> http://www.trelby.org/files/release/font/courier10point.zip +# --> https://web.archive.org/web/20140326080337/http://www.trelby.org/files/release/font/courier10point.zip + +# https://news.ycombinator.com/item?id=18802628 +# -> https://www.trelby.org/assets/courier10point.zip + +# => courier10point.zip + +if [ ! -f cour10p_b.ttf ]; then + + unzip -p courier10point.zip courier10point/cour10p_b.ttf > cour10p_b.ttf + +fi + + +# Construction de l'image de fond +#-------------------------------- + +if [ $# -lt 1 -o $# -gt 2 ]; then + + echo "Usage: $0 <version> <edition>" + exit 123 + +fi + +python3 ./build.py $* + +cp bg.png ../../src/data/images/about-bg.png + +popd > /dev/null diff --git a/tools/about/logo.png b/tools/about/logo.png Binary files differnew file mode 100644 index 0000000..9bf9969 --- /dev/null +++ b/tools/about/logo.png diff --git a/tools/about/noise.png b/tools/about/noise.png Binary files differnew file mode 100644 index 0000000..b5d6b91 --- /dev/null +++ b/tools/about/noise.png diff --git a/tools/about/ttf.py b/tools/about/ttf.py new file mode 100644 index 0000000..eb6c027 --- /dev/null +++ b/tools/about/ttf.py @@ -0,0 +1,111 @@ + +# Source: https://www.cairographics.org/cookbook/freetypepython/ + +import ctypes as ct +import cairo + +_initialized = False +def create_cairo_font_face_for_file (filename, faceindex=0, loadoptions=0): + "given the name of a font file, and optional faceindex to pass to FT_New_Face" \ + " and loadoptions to pass to cairo_ft_font_face_create_for_ft_face, creates" \ + " a cairo.FontFace object that may be used to render text with that font." + global _initialized + global _freetype_so + global _cairo_so + global _ft_lib + global _ft_destroy_key + global _surface + + CAIRO_STATUS_SUCCESS = 0 + FT_Err_Ok = 0 + + if not _initialized: + # find shared objects + _freetype_so = ct.CDLL("libfreetype.so.6") + _cairo_so = ct.CDLL("libcairo.so.2") + _cairo_so.cairo_ft_font_face_create_for_ft_face.restype = ct.c_void_p + _cairo_so.cairo_ft_font_face_create_for_ft_face.argtypes = [ ct.c_void_p, ct.c_int ] + _cairo_so.cairo_font_face_get_user_data.restype = ct.c_void_p + _cairo_so.cairo_font_face_get_user_data.argtypes = (ct.c_void_p, ct.c_void_p) + _cairo_so.cairo_font_face_set_user_data.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p) + _cairo_so.cairo_set_font_face.argtypes = [ ct.c_void_p, ct.c_void_p ] + _cairo_so.cairo_font_face_status.argtypes = [ ct.c_void_p ] + _cairo_so.cairo_font_face_destroy.argtypes = (ct.c_void_p,) + _cairo_so.cairo_status.argtypes = [ ct.c_void_p ] + # initialize freetype + _ft_lib = ct.c_void_p() + status = _freetype_so.FT_Init_FreeType(ct.byref(_ft_lib)) + if status != FT_Err_Ok : + raise RuntimeError("Error %d initializing FreeType library." % status) + #end if + + class PycairoContext(ct.Structure): + _fields_ = \ + [ + ("PyObject_HEAD", ct.c_byte * object.__basicsize__), + ("ctx", ct.c_void_p), + ("base", ct.c_void_p), + ] + #end PycairoContext + + _surface = cairo.ImageSurface(cairo.FORMAT_A8, 0, 0) + _ft_destroy_key = ct.c_int() # dummy address + _initialized = True + #end if + + ft_face = ct.c_void_p() + cr_face = None + try : + # load FreeType face + status = _freetype_so.FT_New_Face(_ft_lib, filename.encode("utf-8"), faceindex, ct.byref(ft_face)) + if status != FT_Err_Ok : + raise RuntimeError("Error %d creating FreeType font face for %s" % (status, filename)) + #end if + + # create Cairo font face for freetype face + cr_face = _cairo_so.cairo_ft_font_face_create_for_ft_face(ft_face, loadoptions) + status = _cairo_so.cairo_font_face_status(cr_face) + if status != CAIRO_STATUS_SUCCESS : + raise RuntimeError("Error %d creating cairo font face for %s" % (status, filename)) + #end if + # Problem: Cairo doesn't know to call FT_Done_Face when its font_face object is + # destroyed, so we have to do that for it, by attaching a cleanup callback to + # the font_face. This only needs to be done once for each font face, while + # cairo_ft_font_face_create_for_ft_face will return the same font_face if called + # twice with the same FT Face. + # The following check for whether the cleanup has been attached or not is + # actually unnecessary in our situation, because each call to FT_New_Face + # will return a new FT Face, but we include it here to show how to handle the + # general case. + if _cairo_so.cairo_font_face_get_user_data(cr_face, ct.byref(_ft_destroy_key)) == None : + status = _cairo_so.cairo_font_face_set_user_data \ + ( + cr_face, + ct.byref(_ft_destroy_key), + ft_face, + _freetype_so.FT_Done_Face + ) + if status != CAIRO_STATUS_SUCCESS : + raise RuntimeError("Error %d doing user_data dance for %s" % (status, filename)) + #end if + ft_face = None # Cairo has stolen my reference + #end if + + # set Cairo font face into Cairo context + cairo_ctx = cairo.Context(_surface) + cairo_t = PycairoContext.from_address(id(cairo_ctx)).ctx + _cairo_so.cairo_set_font_face(cairo_t, cr_face) + status = _cairo_so.cairo_font_face_status(cairo_t) + if status != CAIRO_STATUS_SUCCESS : + raise RuntimeError("Error %d creating cairo font face for %s" % (status, filename)) + #end if + + finally : + _cairo_so.cairo_font_face_destroy(cr_face) + _freetype_so.FT_Done_Face(ft_face) + #end try + + # get back Cairo font face as a Python object + face = cairo_ctx.get_font_face() + return face +#end create_cairo_font_face_for_file diff --git a/tools/maint/exportall.inc b/tools/maint/exportall.inc new file mode 100644 index 0000000..f03be3c --- /dev/null +++ b/tools/maint/exportall.inc @@ -0,0 +1,8 @@ + +# source tools/maint/exportall.inc + +# unset PYTHONPATH GSETTINGS_SCHEMA_DIR LD_LIBRARY_PATH + +source tools/maint/pypath.inc +source tools/maint/gsettings.inc +source tools/maint/libpath.inc diff --git a/tools/maint/gsettings.inc b/tools/maint/gsettings.inc index 40941b4..85614c6 100644 --- a/tools/maint/gsettings.inc +++ b/tools/maint/gsettings.inc @@ -3,6 +3,6 @@ # source tools/maint/gsettings.inc -glib-compile-schemas src/schemas/ +find . -type d -name schemas -exec glib-compile-schemas {} \; -export GSETTINGS_SCHEMA_DIR=$PWD/src/schemas +export GSETTINGS_SCHEMA_DIR=$( find . -type d -name schemas -printf "$PWD/%P:" | sed 's/:$//' ) diff --git a/tools/maint/libpath.inc b/tools/maint/libpath.inc new file mode 100644 index 0000000..19e68d7 --- /dev/null +++ b/tools/maint/libpath.inc @@ -0,0 +1,10 @@ + +# Look for all path containing binaries for local execution + +# source tools/maint/libpath.inc + +LD_LIBRARY_PATH="$PWD/src/.libs:$LD_LIBRARY_PATH" + +find . -type d -name schemas -exec glib-compile-schemas {} \; + +export LD_LIBRARY_PATH=$( find plugins/ -maxdepth 2 -type d -name '.libs' -printf "$PWD/plugins/%P:" | sed 's/:$//' ):"$LD_LIBRARY_PATH" |
