diff --git a/pd/doc/5.reference/preset_hub-help.pd b/pd/doc/5.reference/preset_hub-help.pd
index bd13dc4064bc2a22b8108a471285d24013e2db7d..de352cdb619daf4e5a9f2688dd38da07057072c0 100644
--- a/pd/doc/5.reference/preset_hub-help.pd
+++ b/pd/doc/5.reference/preset_hub-help.pd
@@ -1,9 +1,9 @@
-#N canvas 138 24 553 616 10;
+#N canvas 138 24 553 651 10;
 #X obj 109 282 cnv 15 60 37 empty empty empty 20 12 0 14 -261234 -66577
 0;
 #X obj 183 294 cnv 15 44 27 empty empty empty 20 12 0 14 -204280 -66577
 0;
-#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
+#X obj 0 1041 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
 14 -228856 -66577 0;
 #X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header preset_hub 3 12
 0 18 -204280 -1 0;
@@ -18,34 +18,31 @@ to conform to the PDDP template for Pd version 0.42.;
 #X text 12 85 OUTLET_0 anything;
 #X text 12 105 AUTHOR Ivica Ico Bukvic (c) 2012 <ico@vt.edu>;
 #X text 12 65 INLET_0 clear purge recall reset store;
-#X restore 500 597 pd META;
-#X obj 0 463 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+#X restore 500 1043 pd META;
+#X obj 0 563 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
 13 -228856 -1 0;
-#X obj 0 498 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+#X obj 0 608 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
 0 13 -228856 -1 0;
-#X obj 0 538 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+#X obj 0 778 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
 0 13 -228856 -1 0;
-#X obj 78 357 cnv 17 3 100 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X obj 78 357 cnv 17 3 200 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
 #N canvas 102 481 428 108 Related_objects 0;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
 14 -204280 -1 0;
 #X obj 22 36 preset_node;
 #X text 7 1 [preset_hub] Related Objects;
-#X restore 101 597 pd Related_objects;
-#X obj 78 472 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X restore 101 1043 pd Related_objects;
+#X obj 78 572 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
-#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
+#X obj 4 1043 pddp/pddplink all_about_help_patches.pd -text Usage Guide
 ;
 #X text 11 20 universal preset system for pd-l2ork;
-#X obj 20 202 preset_hub test %hidden% %node% 1 2 %preset% 1 30 %preset%
-0 15 %node% 1 10 %preset% 1 2.24 nlah 1 %preset% 0 a b c 4 3 2 1 %node%
-3 13 0 0 %preset% 1 1.02 %preset% 0 0.01 %node% 2 20 0 %preset% 1 two
-%preset% 0 zero %node% 1 25 %node% 2 44 0 %node% 1 48 %node% 3 73 0
-0 %node% 2 54 0 %node% 1 21 %preset% 1 99 %preset% 2 23 %preset% 3
-1001 %preset% 0 3 %node% 2 31 1 %preset% 1 problems %preset% 2 skidoo
-%preset% 3 nights %preset% 0 stooges %node% 1 33 %node% 2 39 0 %node%
-1 64;
+#X obj 20 202 preset_hub test %hidden% %node% 2 31 1 %preset% 1 problems
+%preset% 2 skidoo %preset% 3 nights %preset% 0 stooges %node% 1 33
+%preset% 0 aaa 32 1 %preset% 1 2.24 nlah 1 %preset% 2 1 2 3 4 %node%
+2 39 0 %preset% 0 1 %preset% 1 5 %preset% 2 9 %node% 1 61 %preset%
+0 22.1 %preset% 1 0.5 %preset% 2 3.14;
 #X msg 80 125 store \$1;
 #X msg 80 74 recall \$1;
 #X msg 30 73 purge;
@@ -61,11 +58,9 @@ to conform to the PDDP template for Pd version 0.42.;
 #X msg 386 60 reset;
 #X msg 447 190 clear \$1;
 #X floatatom 447 166 5 0 0 0 - - -;
-#X obj 386 217 preset_hub another %hidden% %node% 2 23 10 %node% 2
-24 0 %node% 1 26 %node% 3 14 0 3 %preset% 0 -17 %preset% 1 382 %preset%
-2 1234 %node% 1 25 %preset% 0 -4 %preset% 1 54 %preset% 2 -999 %node%
-3 2 0 3 %node% 1 6 %node% 3 1 0 3 %node% 1 5 %node% 1 23 %node% 1 45
-%node% 3 73 0 3 %node% 2 54 2 %node% 1 32 %node% 2 39 2;
+#X obj 386 217 preset_hub another %hidden% %node% 1 32 %preset% 0 99
+%preset% 1 65 %preset% 2 123 %preset% 3 0 %node% 2 39 2 %preset% 0
+19 %preset% 1 0.23 %preset% 2 999;
 #X obj 386 244 print hub_another_says;
 #X obj 20 268 hub_node_abs;
 #X obj 386 270 preset_node another;
@@ -75,7 +70,7 @@ to conform to the PDDP template for Pd version 0.42.;
 #X msg 236 91 2.24 nlah 1;
 #X obj 188 157 print ----result----;
 #X msg 241 111 list 1 2 3 4;
-#N canvas 0 0 404 229 sub 0;
+#N canvas 444 419 404 229 sub 0;
 #X obj 237 69 preset_node test;
 #X floatatom 237 92 5 0 0 0 - - -;
 #X obj 113 69 preset_node another;
@@ -84,14 +79,14 @@ to conform to the PDDP template for Pd version 0.42.;
 #X connect 0 0 1 0;
 #X connect 2 0 3 0;
 #X restore 119 292 pd sub;
-#X text 98 418 reset;
-#X text 99 472 anything;
+#X text 98 416 reset;
+#X text 99 572 anything;
 #X obj 425 21 pddp/pddplink http://wiki.puredata.info/en/preset_hub
 -text pdpedia: preset_hub;
-#X text 99 376 purge;
-#X text 98 397 recall;
-#X text 98 438 store;
-#X text 81 515 1) symbol;
+#X text 98 376 purge;
+#X text 98 396 recall;
+#X text 98 436 store;
+#X text 81 625 1) symbol;
 #X obj 381 305 cnv 15 44 27 empty empty empty 20 12 0 14 -204280 -66577
 0;
 #X floatatom 386 310 5 0 0 0 - - -;
@@ -101,12 +96,50 @@ to conform to the PDDP template for Pd version 0.42.;
 #X floatatom 80 151 5 0 0 0 - - -;
 #X obj 447 58 hradio 15 1 0 4 empty empty empty 0 -8 0 10 -262144 -1
 -1 0;
-#X obj 98 573 pddp/pddplink preset_node-help.pd;
-#X text 168 397 - recall the values stored at index n;
-#X text 168 438 - store the current state at index n;
-#X text 169 472 - print message in response to input;
-#X text 98 545 Scope- [preset_hub foo] is local to its patch and all
+#X obj 98 813 pddp/pddplink preset_node-help.pd;
+#X text 168 396 - recall the values stored at index n;
+#X text 168 436 - store the current state at index n;
+#X text 98 785 Scope- [preset_hub foo] is local to its patch and all
 of that patch's children (i.e. \, subpatches and abstractions).;
+#X text 168 376 - purge all orphaned/unused nodes;
+#X text 98 356 clear;
+#X text 168 356 - clears preset n for all nodes;
+#X obj 479 3 preset_hub  %hidden% %node% 2 10 1;
+#X obj 189 271 preset_node test;
+#X floatatom 189 299 5 0 0 0 - - -;
+#X msg 198 211 purge;
+#X msg 189 189 reset;
+#X msg 249 210 clearall \$1;
+#X floatatom 249 189 5 0 0 0 - - -;
+#X obj 233 300 print middle_node;
+#X msg 232 244 set another;
+#X msg 311 244 set test;
+#X text 171 625 - OPTIONAL scope for this preset (defaults to null
+or "");
+#X text 168 416 - reset all preset node data (wipes hub clean);
+#X text 98 456 read;
+#X text 169 572 - print message in response to input followed by 1
+for successful and 0 for failed operation;
+#X text 81 645 2) symbol file;
+#X text 81 735 3) float;
+#X text 171 645 - OPTIONAL literal "file" argument instructs hub not
+to embed its preset data into the patch but rather assumes that the
+user will save this data into an external file (see read \, write \,
+readpreset \, and writepreset message above)--this also means that
+changes to the preset will not trigger "patch dirty" message;
+#X text 171 735 - OPTIONAL argument used exclusively in the K12 mode
+that renders preset_hub invisible to GUI operations;
+#X text 98 476 write;
+#X text 98 496 readpreset;
+#X text 168 476 - write all presets to a file named in the second argument
+;
+#X text 168 456 - reads all presets from a file named in the second
+argument;
+#X text 168 496 - read one preset (optional 3rd argumemnt \, otherwise
+preset 0) from a file named in the second argument;
+#X text 98 526 writepreset;
+#X text 168 526 - write one preset (optional 3rd argumemnt \, otherwise
+preset 0) to a file named in the second argument;
 #N canvas 186 348 428 292 More_info 0;
 #X obj 30 165 cnv 15 349 98 empty \$0-pddp.cnv.subheading empty 3 12
 0 14 -191407 -1 0;
@@ -127,23 +160,71 @@ same canvas as the abstraction:;
 #X text 168 205 inside it.;
 #X text 40 171 [preset_hub foo] <-- this won't apply to [bar] because
 ;
-#X restore 461 551 pd More_info;
-#X text 168 376 - purge all orphaned/unused nodes;
-#X text 168 418 - reset all preset node data;
-#X text 98 356 clear;
-#X text 168 356 - clears preset n for all nodes;
-#X obj 479 3 preset_hub  %hidden% %node% 2 10 1;
-#X obj 189 271 preset_node test;
-#X floatatom 189 299 5 0 0 0 - - -;
-#X msg 198 211 purge;
-#X msg 189 189 reset;
-#X msg 249 210 clearall \$1;
-#X floatatom 249 189 5 0 0 0 - - -;
-#X obj 233 300 print middle_node;
-#X msg 232 244 set another;
-#X msg 311 244 set test;
-#X text 171 515 - OPTIONAL scope for this preset (defaults to null
-or "");
+#X restore 461 791 pd More_info;
+#N canvas 512 381 640 292 More_info 0;
+#X obj -9 -1 cnv 15 639 20 empty \$0-pddp.cnv.subheading empty 3 12
+0 14 -204280 -1 0;
+#X text -3 -1 [preset_hub] More info on reading/writing operations
+;
+#X obj 9 192 preset_hub another file %hidden%;
+#X obj 19 139 hradio 15 1 0 4 empty empty empty 0 -8 0 10 -262144 -1
+-1 0;
+#X msg 19 159 recall \$1;
+#X obj 85 139 hradio 15 1 0 4 empty empty empty 0 -8 0 10 -262144 -1
+-1 0;
+#X msg 85 159 store \$1;
+#X obj 10 238 preset_node another;
+#X floatatom 10 260 5 0 0 0 - - -;
+#X obj 135 238 preset_node another;
+#X symbolatom 135 260 10 0 0 0 - - -;
+#X text 263 24 First \, click on read to read all presets stored in
+a separate file. Try recalling presets 0 and 1 Note how storing a new
+preset does not make the patch dirty due to use of the "file" flag.
+;
+#X msg 151 165 reset;
+#X text 263 82 Now reset the hub by clicking on the "reset" message.
+The hub should be now clear of any presets. Now read a single preset
+by clicking on readpreset message on the left. Notice how only preset
+1 has any data (in this case identical to that found in all presets
+file we loaded using the read call above.;
+#X text 263 166 Loading a preset into a hub that already has data will
+automatically result in a merge. As you experiment with mixing and
+merging various presets \, notice how nodes in a preset that do not
+have the right match in the current patch will be silently discarded
+to minimize clutter among presets.;
+#X msg 9 28 read preset_hub_all.preset;
+#X msg 9 83 readpreset preset_hub_one.preset;
+#X msg 9 104 writepreset /tmp/preset_hub_one.preset 1;
+#X msg 9 49 write /tmp/preset_hub_all.preset;
+#X obj 9 213 print preset_hub_another_says;
+#X text 263 250 NB: check for write permissions before trying to save
+files into specific folders.;
+#X connect 2 0 19 0;
+#X connect 3 0 4 0;
+#X connect 4 0 2 0;
+#X connect 5 0 6 0;
+#X connect 6 0 2 0;
+#X connect 7 0 8 0;
+#X connect 9 0 10 0;
+#X connect 12 0 2 0;
+#X connect 15 0 2 0;
+#X connect 16 0 2 0;
+#X connect 17 0 2 0;
+#X connect 18 0 2 0;
+#X restore 461 841 pd More_info;
+#X text 98 833 Writing to and reading from a file- preset_hub can also
+write to an external file and read from it. More so \, the optional
+2nd creation argument (literal symbol "file") prevents preset_hub from
+trying to save itself into the patch. Hub can read/write all preset
+states (read/write) or a select preset (readpreset/writepreset). Resetting
+of hub's previously stored data must be done manually by the user.
+Otherwise \, loaded data will be merged with the data already found
+in the hub. Only valid nodes will be imported. Node data that does
+not have a valid node in the existing patch will be silently dropped.
+Reading a single preset will automatically recall it as soon as the
+preset has been loaded.;
+#X text 98 1007 Default values- messages when supplied no argument
+default to 0 or empty string;
 #X connect 14 0 20 0;
 #X connect 15 0 14 0;
 #X connect 16 0 14 0;
@@ -168,11 +249,11 @@ or "");
 #X connect 50 0 15 0;
 #X connect 51 0 19 0;
 #X connect 52 0 23 0;
-#X connect 64 0 70 0;
-#X connect 64 0 65 0;
-#X connect 66 0 64 0;
-#X connect 67 0 64 0;
-#X connect 68 0 64 0;
-#X connect 69 0 68 0;
-#X connect 71 0 64 0;
-#X connect 72 0 64 0;
+#X connect 61 0 67 0;
+#X connect 61 0 62 0;
+#X connect 63 0 61 0;
+#X connect 64 0 61 0;
+#X connect 65 0 61 0;
+#X connect 66 0 65 0;
+#X connect 68 0 61 0;
+#X connect 69 0 61 0;
diff --git a/pd/doc/5.reference/preset_node-help.pd b/pd/doc/5.reference/preset_node-help.pd
index 157e1012d4243661d091bd27bc51863ae659fa4e..e785cd806b0c6c0e934be65aed721ead5beb132c 100644
--- a/pd/doc/5.reference/preset_node-help.pd
+++ b/pd/doc/5.reference/preset_node-help.pd
@@ -1,9 +1,9 @@
-#N canvas 357 24 555 678 10;
-#X obj 178 291 cnv 15 44 27 empty empty empty 20 12 0 14 -204280 -66577
+#N canvas 135 24 553 653 10;
+#X obj 180 291 cnv 15 44 27 empty empty empty 20 12 0 14 -204280 -66577
 0;
 #X obj 105 279 cnv 15 60 37 empty empty empty 20 12 0 14 -261234 -66577
 0;
-#X obj -2 655 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
+#X obj -2 865 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
 14 -228856 -66577 0;
 #X obj -2 0 cnv 15 552 40 empty \$0-pddp.cnv.header preset_node 3 12
 0 18 -204280 -1 0;
@@ -18,35 +18,34 @@ to conform to the PDDP template for Pd version 0.42.;
 #X text 12 65 INLET_0 anything;
 #X text 12 85 OUTLET_0 anything;
 #X text 12 105 AUTHOR Ivica Ico Bukvic (c) 2012 <ico@vt.edu>;
-#X restore 498 657 pd META;
-#X obj -2 493 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+#X restore 498 867 pd META;
+#X obj -2 633 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
 13 -228856 -1 0;
-#X obj -2 548 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+#X obj -2 688 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
 0 13 -228856 -1 0;
-#X obj -2 598 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8
+#X obj -2 738 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8
 12 0 13 -228856 -1 0;
-#X obj 76 347 cnv 17 3 140 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X obj 76 347 cnv 17 3 280 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
 #N canvas 102 481 428 108 Related_objects 0;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
 14 -204280 -1 0;
 #X text 7 1 [preset_node] Related Objects;
 #X obj 22 36 preset_hub default %hidden%;
-#X restore 99 657 pd Related_objects;
-#X obj 76 502 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X restore 99 867 pd Related_objects;
+#X obj 76 642 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
-#X obj 2 657 pddp/pddplink all_about_help_patches.pd -text Usage Guide
+#X obj 2 867 pddp/pddplink all_about_help_patches.pd -text Usage Guide
 ;
 #X text 9 20 universal preset system for pd-l2ork;
 #X obj 472 3 preset_node;
 #X obj 418 21 pddp/pddplink http://wiki.puredata.info/en/preset_node
 -text pdpedia: preset_node;
-#X obj 18 202 preset_hub test %hidden% %node% 1 2 %preset% 1 30 %preset%
-0 15 %node% 1 10 %preset% 1 2.24 nlah 1 %preset% 0 a b c 4 3 2 1 %node%
-3 13 0 0 %preset% 1 1.02 %preset% 0 0.01 %node% 2 20 0 %preset% 1 two
-%preset% 0 zero %node% 1 27 %node% 2 46 0 %node% 1 48 %node% 3 73 0
-0 %node% 2 54 0 %node% 2 44 1 %node% 1 26 %node% 2 43 1 %node% 1 46
-%node% 2 52 0;
+#X obj 18 202 preset_hub test %hidden% %node% 1 26 %preset% 1 55.2
+%preset% 2 21.4 %preset% 0 123 %node% 2 43 1 %preset% 0 zero %preset%
+1 one %preset% 2 two %node% 1 46 %preset% 0 aaa 32 1 %preset% 1 2.24
+nlah 1 %preset% 2 1 2 3 4 %node% 2 52 0 %preset% 0 0 %preset% 1 1 %preset%
+2 2;
 #X msg 78 74 store \$1;
 #X msg 78 124 recall \$1;
 #X floatatom 78 50 5 0 0 0 - - -;
@@ -56,38 +55,36 @@ to conform to the PDDP template for Pd version 0.42.;
 #X msg 79 174 clear \$1;
 #X floatatom 79 150 5 0 0 0 - - -;
 #X obj 18 231 print hub_test_says;
-#X obj 184 267 preset_node test;
-#X floatatom 184 295 5 0 0 0 - - -;
-#X msg 193 213 purge;
-#X msg 184 191 reset;
-#X msg 244 212 clearall \$1;
-#X floatatom 244 191 5 0 0 0 - - -;
-#X obj 374 50 cnv 15 156 279 empty empty empty 20 12 0 14 -233017 -66577
+#X obj 186 267 preset_node test;
+#X floatatom 186 295 5 0 0 0 - - -;
+#X msg 195 213 purge;
+#X msg 143 190 reset;
+#X msg 246 212 clearall \$1;
+#X floatatom 246 191 5 0 0 0 - - -;
+#X obj 377 50 cnv 15 156 279 empty empty empty 20 12 0 14 -233017 -66577
 0;
-#X msg 445 83 store \$1;
-#X msg 445 134 recall \$1;
-#X floatatom 445 59 5 0 0 0 - - -;
-#X floatatom 445 110 5 0 0 0 - - -;
-#X msg 393 82 purge;
-#X msg 384 60 reset;
-#X msg 445 184 clear \$1;
-#X floatatom 445 160 5 0 0 0 - - -;
-#X obj 384 217 preset_hub another %hidden% %node% 2 23 10 %node% 2
-24 0 %node% 1 26 %node% 3 14 0 3 %preset% 0 -17 %preset% 1 382 %preset%
-2 1234 %node% 1 25 %preset% 0 -4 %preset% 1 54 %preset% 2 -999 %node%
-3 2 0 3 %node% 1 6 %node% 3 1 0 3 %node% 1 5 %node% 1 23 %node% 1 45
-%node% 3 73 0 3 %node% 2 54 2 %node% 1 44 %node% 2 52 2;
-#X obj 384 244 print hub_another_says;
+#X msg 448 83 store \$1;
+#X msg 448 134 recall \$1;
+#X floatatom 448 59 5 0 0 0 - - -;
+#X floatatom 448 110 5 0 0 0 - - -;
+#X msg 396 82 purge;
+#X msg 387 60 reset;
+#X msg 448 184 clear \$1;
+#X floatatom 448 160 5 0 0 0 - - -;
+#X obj 387 217 preset_hub another %hidden% %node% 1 44 %preset% 0 -5.1
+%preset% 1 22 %preset% 2 1.001 %node% 2 52 2 %preset% 0 41 %preset%
+1 101 %preset% 2 7.13;
+#X obj 387 244 print hub_another_says;
 #X obj 18 263 hub_node_abs;
-#X obj 384 270 preset_node another;
-#X obj 229 295 print middle_node;
-#X obj 183 55 preset_node test;
-#X obj 183 139 list;
-#X msg 221 75 aaa 32 1;
-#X msg 231 95 2.24 nlah 1;
-#X obj 183 161 print ----result----;
-#X msg 236 115 list 1 2 3 4;
-#N canvas 0 0 404 229 sub 0;
+#X obj 387 270 preset_node another;
+#X obj 231 295 print middle_node;
+#X obj 185 55 preset_node test;
+#X obj 185 139 list;
+#X msg 223 75 aaa 32 1;
+#X msg 233 95 2.24 nlah 1;
+#X obj 185 161 print ----result----;
+#X msg 238 115 list 1 2 3 4;
+#N canvas 523 458 404 229 sub 0;
 #X obj 237 69 preset_node test;
 #X floatatom 237 92 5 0 0 0 - - -;
 #X obj 113 69 preset_node another;
@@ -97,15 +94,15 @@ to conform to the PDDP template for Pd version 0.42.;
 #X connect 2 0 3 0;
 #X restore 115 289 pd sub;
 #X text 96 366 clearall;
-#X text 97 386 purge;
-#X text 97 502 anything;
-#X text 167 502 - [preset_node] makes an invisible connection from
+#X text 96 386 purge;
+#X text 97 642 anything;
+#X text 167 642 - [preset_node] makes an invisible connection from
 the object below it back to its inlet. It can recall whatever type
 of message the connected object can output.;
-#X text 79 565 1) symbol;
-#X text 96 605 Scope- [preset_hub foo] is local to its patch and all
+#X text 79 705 1) symbol;
+#X text 96 745 Scope- [preset_hub foo] is local to its patch and all
 of that patch's children (i.e. \, subpatches and abstractions).;
-#X obj 96 633 pddp/pddplink preset_hub-help.pd;
+#X obj 96 773 pddp/pddplink preset_hub-help.pd;
 #N canvas 411 177 428 483 More_info 0;
 #X obj 271 378 cnv 15 39 23 empty empty empty 20 12 0 14 -261234 -66577
 0;
@@ -157,7 +154,7 @@ hub.;
 #X connect 17 0 20 0;
 #X connect 18 0 19 0;
 #X connect 19 0 21 0;
-#X restore 457 610 pd More_info;
+#X restore 457 750 pd More_info;
 #X text 166 386 - purge all orphaned/unused nodes (forwarded to hub)
 ;
 #X text 96 427 reset;
@@ -175,13 +172,33 @@ hub);
 ;
 #X text 96 447 set;
 #X text 166 447 - change context (hub) node is paired with;
-#X msg 227 242 set another;
-#X msg 306 242 set test;
-#X obj 378 293 cnv 15 44 27 empty empty empty 20 12 0 14 -204280 -66577
+#X msg 229 242 set another;
+#X msg 308 242 set test;
+#X obj 381 293 cnv 15 44 27 empty empty empty 20 12 0 14 -204280 -66577
 0;
-#X floatatom 384 298 5 0 0 0 - - -;
-#X text 169 565 - OPTIONAL scope for this preset (defaults to null
+#X floatatom 387 298 5 0 0 0 - - -;
+#X text 169 705 - OPTIONAL scope for this preset (defaults to null
 or "");
+#X text 96 486 read;
+#X text 96 516 write;
+#X text 96 546 readpreset;
+#X text 96 586 writepreset;
+#X text 166 486 - reads all presets from a file named in the second
+argument (forwarded to hub);
+#X text 166 516 - write all presets to a file named in the second argument
+(forwarded to hub);
+#X text 166 546 - read one preset (optional 3rd argumemnt \, otherwise
+preset 0) from a file named in the second argument (forwarded to hub)
+;
+#X text 166 586 - write one preset (optional 3rd argumemnt \, otherwise
+preset 0) to a file named in the second argument (forwarded to hub)
+;
+#X text 96 795 Reading and writing- see preset_hub-help.pd for more
+info.;
+#X obj 96 809 pddp/pddplink preset_hub-help.pd;
+#X msg 186 190 clear 0;
+#X text 96 827 Default values- messages when supplied no argument default
+to 0 or empty string;
 #X connect 16 0 25 0;
 #X connect 17 0 16 0;
 #X connect 18 0 16 0;
@@ -214,3 +231,4 @@ or "");
 #X connect 51 0 47 0;
 #X connect 73 0 26 0;
 #X connect 74 0 26 0;
+#X connect 88 0 26 0;
diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h
index 1bb8c9b984c008eb6a63e7c2de889082fa415a95..94a19562d3a309f051264c12576a8522d514207f 100644
--- a/pd/src/m_pd.h
+++ b/pd/src/m_pd.h
@@ -11,7 +11,7 @@ extern "C" {
 #define PD_MAJOR_VERSION 0
 #define PD_MINOR_VERSION 42
 #define PD_BUGFIX_VERSION 7
-#define PD_TEST_VERSION "20130126"
+#define PD_TEST_VERSION "20130523"
 #define PDL2ORK
 
 /* old name for "MSW" flag -- we have to take it for the sake of many old
diff --git a/pd/src/x_preset.c b/pd/src/x_preset.c
index 5bfa69f917273ad3e0c39d0de5ddaf9008270601..353e4dae1088123972562624bee85d23aef1e1d6 100644
--- a/pd/src/x_preset.c
+++ b/pd/src/x_preset.c
@@ -46,13 +46,16 @@
 void preset_hub_add_a_node(t_preset_hub *h, t_preset_node *x);
 void preset_hub_recall(t_preset_hub *h, t_float f);
 void preset_hub_store(t_preset_hub *h, t_float f);
-void preset_hub_add_a_node(t_preset_hub *h, t_preset_node *x);
 void preset_hub_delete_a_node(t_preset_hub *h, t_preset_node *x);
 void preset_node_seek_hub(t_preset_node *x);
 int  preset_hub_compare_loc(int *h_loc, int h_loc_length, int *n_loc, int n_loc_length);
 void preset_hub_reset(t_preset_hub *h);
 void preset_hub_purge(t_preset_hub *h);
 void preset_hub_clear(t_preset_hub *x, t_float f);
+void preset_hub_read(t_preset_hub *x, t_symbol *filename);
+void preset_hub_write(t_preset_hub *x, t_symbol *filename);
+void preset_hub_readpreset(t_preset_hub *x, t_symbol *filename);
+void preset_hub_writepreset(t_preset_hub *x, t_symbol *filename, float preset);
 
 static int preset_node_location_changed(t_preset_node *x);
 static void preset_node_update_my_glist_location(t_preset_node *x);
@@ -452,6 +455,30 @@ void preset_node_request_hub_store(t_preset_node *x, t_float f)
 		preset_hub_store(x->pn_hub, f);
 }
 
+void preset_node_request_hub_read(t_preset_node *x, t_symbol *filename)
+{
+	if (x->pn_hub)
+		preset_hub_read(x->pn_hub, filename);
+}
+
+void preset_node_request_hub_write(t_preset_node *x, t_symbol *filename)
+{
+	if (x->pn_hub)
+		preset_hub_write(x->pn_hub, filename);
+}
+
+void preset_node_request_hub_readpreset(t_preset_node *x, t_symbol *filename)
+{
+	if (x->pn_hub)
+		preset_hub_readpreset(x->pn_hub, filename);
+}
+
+void preset_node_request_hub_writepreset(t_preset_node *x, t_symbol *filename, float preset)
+{
+	if (x->pn_hub)
+		preset_hub_writepreset(x->pn_hub, filename, preset);
+}
+
 void preset_node_set_and_output_value(t_preset_node *x, t_alist val)
 {
 	if(PH_DEBUG) fprintf(stderr,"preset_node_set_and_output_value %lx\n", (t_int)x);
@@ -518,11 +545,11 @@ void preset_node_clear(t_preset_node *x, t_float f)
 			}
 		}
 	}
-	if (changed) canvas_dirty(x->pn_hub->ph_canvas, 1);
+	if (changed && !x->pn_hub->ph_extern_file) canvas_dirty(x->pn_hub->ph_canvas, 1);
 
 	SETFLOAT(ap+0, f);
 	SETFLOAT(ap+1, (t_float)changed);
-	outlet_anything(x->pn_outlet, gensym("node_clear"), 2, ap);
+	outlet_anything(x->pn_hub->ph_outlet, gensym("node_clear"), 2, ap);
 }
 
 
@@ -647,6 +674,15 @@ void preset_node_setup(void)
     class_addmethod(preset_node_class, (t_method)preset_node_purge,
         gensym("purge"), A_NULL, 0);
 
+    class_addmethod(preset_node_class, (t_method)preset_node_request_hub_read,
+        gensym("read"), A_DEFSYM, 0);
+    class_addmethod(preset_node_class, (t_method)preset_node_request_hub_write,
+        gensym("write"), A_DEFSYM, 0);
+    class_addmethod(preset_node_class, (t_method)preset_node_request_hub_readpreset,
+        gensym("readpreset"), A_DEFSYM, A_DEFFLOAT, 0);
+    class_addmethod(preset_node_class, (t_method)preset_node_request_hub_writepreset,
+        gensym("writepreset"), A_DEFSYM, A_DEFFLOAT, 0);
+
 	// we use anything to cover virtually all presetable types of data
 	class_addanything(preset_node_class, preset_node_anything);
 }
@@ -668,7 +704,7 @@ typedef enum
 
 /*	syntax for saving a preset hub (all in a single line, here it is
 	separated for legibility sakes):
-	#X obj X Y preset_hub NAME %hidden%
+	#X obj X Y preset_hub NAME(optional) file(optional) 1(optional) %hidden%
 	%node% LOCATION_ARRAY_LENGTH LOCATION_ARRAY_(INT) 1 2 3 etc.
 	%preset% 1 data
 	%preset% 2 4
@@ -681,6 +717,12 @@ typedef enum
 
 	NB: %hidden% is used to hide optional arguments following that argument
 		it can be used by any other object as well
+
+	file(optional) arg is used to tell hub it will be dealing exclusively with
+	external files (this prevents dirty flag when storing new presets)
+	
+	1(optional) arg is used for k12 mode to prevent copying and pasting
+	multiple hubs with same names
 */
 
 void preset_hub_save(t_gobj *z, t_binbuf *b)
@@ -696,41 +738,49 @@ void preset_hub_save(t_gobj *z, t_binbuf *b)
 	binbuf_addv(b, "ssiiss", gensym("#X"), gensym("obj"), (int)x->ph_obj.te_xpix,
 				(int)x->ph_obj.te_ypix, gensym("preset_hub"), x->ph_name);
 
+	if (x->ph_extern_file)
+		binbuf_addv(b, "s", gensym("file"));
+
 	if (x->ph_invis)
 		binbuf_addv(b, "i", (int)x->ph_invis);
 
 	binbuf_addv(b, "s", gensym("%hidden%"));
 
-	phd = x->ph_data;
-	while (phd) {
-		if(PH_DEBUG) fprintf(stderr,"	saving phd\n");
-		// designate a node and state whether it is active or disabled
-		// (disabled nodes are ones that have presets saved but have been deleted since--
-		// we keep these in the case of undo actions during the session that may go beyond
-		// saving something into a file)
-		binbuf_addv(b, "si", gensym("%node%"), phd->phd_pn_gl_loc_length);
-
-		// gather info about the length of the node's location and store it
-		for (i = 0; i < phd->phd_pn_gl_loc_length; i++) {
-			binbuf_addv(b,"i", (int)phd->phd_pn_gl_loc[i]);
-		}
+	// save preset data with the patch only if we are not
+	// saving into an external file
+	if (!x->ph_extern_file) {
+
+		phd = x->ph_data;
+		while (phd) {
+			if(PH_DEBUG) fprintf(stderr,"	saving phd\n");
+			// designate a node and state whether it is active or disabled
+			// (disabled nodes are ones that have presets saved but have been deleted since--
+			// we keep these in the case of undo actions during the session that may go beyond
+			// saving something into a file)
+			binbuf_addv(b, "si", gensym("%node%"), phd->phd_pn_gl_loc_length);
+
+			// gather info about the length of the node's location and store it
+			for (i = 0; i < phd->phd_pn_gl_loc_length; i++) {
+				binbuf_addv(b,"i", (int)phd->phd_pn_gl_loc[i]);
+			}
 
-		// save preset data
-		np = phd->phd_npreset;
-		while (np) {
-			if (np->np_val.l_n > 0) {
-				binbuf_addv(b, "si", gensym("%preset%"), (int)np->np_preset);
-				for (i = 0; i < np->np_val.l_n; i++) {
-					if (np->np_val.l_vec[i].l_a.a_type == A_FLOAT)
-						binbuf_addv(b, "f", np->np_val.l_vec[i].l_a.a_w.w_float);
-					else if (np->np_val.l_vec[i].l_a.a_type == A_SYMBOL)
-						binbuf_addv(b, "s", np->np_val.l_vec[i].l_a.a_w.w_symbol);	
+			// save preset data
+			np = phd->phd_npreset;
+			while (np) {
+				if (np->np_val.l_n > 0) {
+					binbuf_addv(b, "si", gensym("%preset%"), (int)np->np_preset);
+					for (i = 0; i < np->np_val.l_n; i++) {
+						if (np->np_val.l_vec[i].l_a.a_type == A_FLOAT)
+							binbuf_addv(b, "f", np->np_val.l_vec[i].l_a.a_w.w_float);
+						else if (np->np_val.l_vec[i].l_a.a_type == A_SYMBOL)
+							binbuf_addv(b, "s", np->np_val.l_vec[i].l_a.a_w.w_symbol);	
+					}
 				}
+				np = np->np_next;
 			}
-			np = np->np_next;
-		}
 
-		phd = phd->phd_next;
+			phd = phd->phd_next;
+		}
 	}
 	if(PH_DEBUG) fprintf(stderr,"	done\n");
 	binbuf_addv(b, ";");
@@ -877,7 +927,7 @@ void preset_hub_store(t_preset_hub *h, t_float f)
 		}
 		canvas_resume_dsp(dspstate);
 
-		if (changed) canvas_dirty(h->ph_canvas, 1);
+		if (changed && !h->ph_extern_file) canvas_dirty(h->ph_canvas, 1);
 
 		SETFLOAT(ap+0, f);
 		outlet_anything(h->ph_outlet, gensym("store"), 1, ap);
@@ -1031,7 +1081,7 @@ void preset_hub_reset(t_preset_hub *h)
 	// and finally request pairing with nodes (since we deleted all our references)
 	glob_preset_node_list_seek_hub();
 
-	if (changed) canvas_dirty(h->ph_canvas, 1);
+	if (changed && !h->ph_extern_file) canvas_dirty(h->ph_canvas, 1);
 
 	SETFLOAT(ap+0, (t_float)changed);
 	outlet_anything(h->ph_outlet, gensym("reset"), 1, ap);
@@ -1083,7 +1133,7 @@ void preset_hub_clear(t_preset_hub *h, t_float f)
 			hd2 = hd2->phd_next;
 		}
 	}
-	if (changed) canvas_dirty(h->ph_canvas, 1);
+	if (changed && !h->ph_extern_file) canvas_dirty(h->ph_canvas, 1);
 
 	SETFLOAT(ap+0, (t_float)changed);
 	outlet_anything(h->ph_outlet, gensym("clear"), 1, ap);
@@ -1136,12 +1186,581 @@ void preset_hub_purge(t_preset_hub *h)
 			}
 		}
 	}
-	if (changed) canvas_dirty(h->ph_canvas, 1);
+	if (changed && !h->ph_extern_file) canvas_dirty(h->ph_canvas, 1);
 
 	SETFLOAT(ap+0, (t_float)changed);
 	outlet_anything(h->ph_outlet, gensym("purge"), 1, ap);
 }
 
+void preset_hub_read(t_preset_hub *x, t_symbol *filename)
+{
+	if(PH_DEBUG) fprintf(stderr,"preset_hub_read\n");
+
+	t_atom ap[1];
+	int result = 0;
+	int pos = 0;
+	t_atom *argv = NULL;
+	t_binbuf *b = NULL;
+	int natom = 0;
+	int i = 0;
+
+	t_preset_hub_data *hd1, *hd2;
+	t_node_preset *np1, *np2;
+	t_hub_parser h_cur = H_NONE;
+	int loc_length = 0;
+	int *loc_data = NULL;
+	int loc_pos = 0;
+	int node_preset = -1;
+	int found_node = 0;
+	int found_preset = 0;
+	int ignore_entry = 0;
+	int data_count = 0;
+
+	if (filename == &s_) {
+		pd_error(x, "no read filename given\n");
+		goto preset_hub_read_fail;
+	}
+
+	// we only try to do this if we have valid paired nodes
+	// otherwise, we'll be filling the hub with bunch of
+	// disabled nodes that are unlikely to be paired properly
+	if (x->ph_data) {
+
+		b = binbuf_new();
+
+		if (binbuf_read_via_canvas(b, filename->s_name, x->ph_canvas, 1)) {
+			pd_error(x, "%s: read failed", filename->s_name);
+		} else {
+			hd1 = NULL;
+			hd2 = NULL;
+			np1 = NULL;
+			np2 = NULL;
+			ignore_entry = 0;
+
+			// load the data from the buffer and add to the preset db
+			natom = binbuf_getnatom(b);
+			if (pos < natom && natom > 1) {
+				argv = binbuf_getvec(b);
+				if (argv->a_type != A_SYMBOL || strcmp(atom_getsymbol(argv)->s_name, "#hub")) {
+					pd_error(x, "%s: malformed file", filename->s_name);
+					goto preset_hub_read_fail;
+				} else {
+					// detected proper preset file format, let's continue
+					argv++;
+					for (pos = 1; pos < natom; pos++, argv++) {
+						if(PH_DEBUG) fprintf(stderr,"	atom\n");
+						// SYMBOL ANALYSIS
+						if (argv->a_type == A_SYMBOL) {
+							if(PH_DEBUG) fprintf(stderr,"	data = %s\n", atom_getsymbol(argv)->s_name);
+							if (!strcmp(atom_getsymbol(argv)->s_name, "%node%")) {
+								// beginning of a new node
+								ignore_entry = 0;
+								if(PH_DEBUG) fprintf(stderr,"	new node\n");
+								loc_length = 0;
+								h_cur = H_NODE;
+							}
+							else if (!strcmp(atom_getsymbol(argv)->s_name, "%preset%") && !ignore_entry) {
+								// beginning of a new preset
+								if(PH_DEBUG) fprintf(stderr,"	new preset\n");
+								h_cur = H_PRESET;
+							}
+						}
+						// FLOAT ANALYSIS
+						else if (argv->a_type == A_FLOAT && !ignore_entry) {
+							if(PH_DEBUG) fprintf(stderr,"	data = %g\n", atom_getfloat(argv));
+							if (h_cur == H_NODE) {
+								// node location length
+								loc_length = (int)atom_getfloat(argv);
+								// reconstruct the dynamic location array
+								if (!loc_data)
+									loc_data = (int*)calloc(loc_length, sizeof(loc_data));
+								loc_data[loc_length-1] = (int)atom_getfloat(&argv[i]);
+								if(PH_DEBUG) fprintf(stderr,"	loc length = %d\n", loc_length);
+								loc_pos = 0;
+								h_cur = H_LOCATION;
+							}
+							else if (h_cur == H_LOCATION) {
+								// node location data
+								loc_data[loc_pos] = (int)atom_getfloat(argv);
+								if(PH_DEBUG) fprintf(stderr,"	loc = %d\n", loc_data[loc_pos]);
+								loc_pos++;
+								if (loc_pos == loc_length) {
+									//the location data has been collected, now check if such a node already exists
+									found_node = 0;
+
+									hd1 = NULL;
+									hd2 = NULL;
+
+									// we use hd1 to navigate and hd2 to populate
+									// first check for existing nodes and reenable them if they match location
+									hd1 = x->ph_data;
+									while (hd1) {
+										if (!preset_hub_compare_loc(hd1->phd_pn_gl_loc, hd1->phd_pn_gl_loc_length,
+											loc_data, loc_length))
+										{
+											// if this hub node data's location matches that of the node
+											if(PH_DEBUG) fprintf(stderr,"	found matching node\n");
+											found_node = 1;
+											break;
+										}
+										hd1 = hd1->phd_next;
+									}
+
+									if (!found_node) {
+										// we failed locating matching ph_data so we ignore the said data
+										// as there is no way the user will be able to predictably recreate
+										// node locations even if they wanted to
+										if(PH_DEBUG) fprintf(stderr,"	failed to find matching node\n");
+										ignore_entry = 1;
+									}
+
+									if (found_node) {
+										hd2 = hd1;
+									}
+
+									// free loc_data as we don't need it any more
+									free(loc_data);
+									loc_data = NULL;
+								}
+							}
+							else if (h_cur == H_PRESET) {
+								// preset number
+								if(PH_DEBUG) fprintf(stderr,"	preset %g\n", atom_getfloat(argv));
+								node_preset = (int)atom_getfloat(argv);
+
+								np1 = NULL;
+								np2 = NULL;
+
+								// check if the preset already exists
+								np1 = hd2->phd_npreset;
+								while (np1 && np1->np_preset != node_preset) {
+									np1 = np1->np_next;
+								}
+								if (!np1) {
+									// we need to create a new preset for the node
+									np2 = (t_node_preset *)t_getbytes(sizeof(*np2));
+									if (!hd2->phd_npreset) {
+										hd2->phd_npreset = np2;
+									} else {
+										np1 = hd2->phd_npreset;
+										while(np1->np_next) {
+											np1 = np1->np_next;
+										}
+										np1->np_next = np2;
+									}
+									np2->np_preset = node_preset;
+								} else {
+									// otherwise we simply make np2 point to np1 (which is our matching preset)
+									np2 = np1;
+									// clear old preset data
+									if (np2->np_val.l_n)
+										alist_clear(&np2->np_val);
+								}
+
+								data_count = 0;
+								argv++;
+
+								// figure out how long of variable data list follows the preset descriptor
+								while (data_count < natom && strcmp(atom_getsymbol(argv+data_count)->s_name, "%preset%") && strcmp(atom_getsymbol(argv+data_count)->s_name, "%node%")) {
+									data_count++;
+								}
+								if(PH_DEBUG) fprintf(stderr,"	found preset? %d found node? %d\n", !strcmp(atom_getsymbol(argv+data_count)->s_name, "%preset%"), !strcmp(atom_getsymbol(argv+data_count)->s_name, "%node%"));
+								if(PH_DEBUG) fprintf(stderr,"	data_count = %d starting @ %d out of %d\n", data_count, pos+1, natom);
+								alist_init(&np2->np_val);
+								alist_list(&np2->np_val, 0, data_count, argv);
+
+								if(PH_DEBUG) {
+									if ((argv)->a_type == A_SYMBOL)
+										fprintf(stderr,"	1st_element = %s\n", atom_getsymbol(argv)->s_name);
+									else if ((argv)->a_type == A_FLOAT)
+										fprintf(stderr,"	1st_element = %f\n", atom_getfloat(argv));
+									else fprintf(stderr,"	1st_element = unknown format\n");
+								}
+
+								pos = pos + data_count;
+								// we already incremented it above before figuring out
+								// how long data list is so we do one less
+								argv = argv + data_count - 1;
+								h_cur = H_PRESET_DATA;
+							}
+						}
+					}
+				}
+			} else {
+				pd_error(x, "%s: malformed/incomplete file", filename->s_name);
+				goto preset_hub_read_fail;
+			}
+		}
+		result = 1;
+	}
+
+	preset_hub_read_fail:
+	if (b) binbuf_free(b);
+	SETFLOAT(ap+0, (t_float)result);
+    outlet_anything(x->ph_outlet, gensym("read"), 1, ap);
+}
+
+void preset_hub_write(t_preset_hub *x, t_symbol *filename)
+{
+	if(PH_DEBUG) fprintf(stderr,"preset_hub_write\n");
+
+	t_atom ap[1];
+	int result = 0;
+	t_atom *outv;
+	int i;
+	t_preset_hub_data *phd;
+	t_node_preset *np;
+	t_binbuf *b = binbuf_new();
+
+	if (filename == &s_) {
+		pd_error(x, "no write filename given\n");
+		goto preset_hub_write_fail;
+	}
+
+	phd = x->ph_data;
+	if (phd) {
+		binbuf_addv(b, "s", gensym("#hub"));
+		while (phd) {
+			if(PH_DEBUG) fprintf(stderr,"	saving phd\n");
+			// designate a node and state whether it is active or disabled
+			// (disabled nodes are ones that have presets saved but have been deleted since--
+			// we keep these in the case of undo actions during the session that may go beyond
+			// saving something into a file)
+			binbuf_addv(b, "si", gensym("%node%"), phd->phd_pn_gl_loc_length);
+
+			// gather info about the length of the node's location and store it
+			for (i = 0; i < phd->phd_pn_gl_loc_length; i++) {
+				binbuf_addv(b,"i", (int)phd->phd_pn_gl_loc[i]);
+			}
+
+			// save preset data
+			np = phd->phd_npreset;
+			while (np) {
+				if (np->np_val.l_n > 0) {
+					binbuf_addv(b, "si", gensym("%preset%"), (int)np->np_preset);
+					for (i = 0; i < np->np_val.l_n; i++) {
+						if (np->np_val.l_vec[i].l_a.a_type == A_FLOAT)
+							binbuf_addv(b, "f", np->np_val.l_vec[i].l_a.a_w.w_float);
+						else if (np->np_val.l_vec[i].l_a.a_type == A_SYMBOL)
+							binbuf_addv(b, "s", np->np_val.l_vec[i].l_a.a_w.w_symbol);	
+					}
+				}
+				np = np->np_next;
+			}
+
+			phd = phd->phd_next;
+		}
+		binbuf_addv(b, ";");
+		result = 1;
+	}
+	if(PH_DEBUG) fprintf(stderr,"	done creating binbuf\n");
+
+    char buf[MAXPDSTRING];
+    canvas_makefilename(x->ph_canvas, filename->s_name,
+		buf, MAXPDSTRING);
+    if (binbuf_write(b, buf, "", 1)) {
+    	pd_error(x, "%s: write failed", filename->s_name);
+		result = 0;
+	}
+
+	preset_hub_write_fail:
+	if (b) binbuf_free(b);
+	if(PH_DEBUG) fprintf(stderr,"	done saving file\n");
+
+	SETFLOAT(ap+0, (t_float)result);
+    outlet_anything(x->ph_outlet, gensym("write"), 1, ap);
+}
+
+void preset_hub_readpreset(t_preset_hub *x, t_symbol *filename)
+{
+	if(PH_DEBUG) fprintf(stderr,"preset_hub_readpreset\n");
+
+	t_atom ap[1];
+	int result = 0;
+	int pos = 0;
+	t_atom *argv = NULL;
+	t_binbuf *b = NULL;
+	int natom = 0;
+	int i = 0;
+
+	t_preset_hub_data *hd1, *hd2;
+	t_node_preset *np1, *np2;
+	t_hub_parser h_cur = H_NONE;
+	int loc_length = 0;
+	int *loc_data = NULL;
+	int loc_pos = 0;
+	int node_preset = -1;
+	int found_node = 0;
+	int found_preset = 0;
+	int ignore_entry = 0;
+	int data_count = 0;
+
+	int recall_preset = -1;
+
+	if (filename == &s_) {
+		pd_error(x, "no readpreset filename given\n");
+		goto preset_hub_readpreset_fail;
+	}
+
+	// we only try to do this if we have valid paired nodes
+	// otherwise, we'll be filling the hub with bunch of
+	// disabled nodes that are unlikely to be paired properly
+	if (x->ph_data) {
+
+		b = binbuf_new();
+
+		if (binbuf_read_via_canvas(b, filename->s_name, x->ph_canvas, 1)) {
+			pd_error(x, "%s: read failed", filename->s_name);
+		} else {
+			hd1 = NULL;
+			hd2 = NULL;
+			np1 = NULL;
+			np2 = NULL;
+			ignore_entry = 0;
+
+			// load the data from the buffer and add to the preset db
+			natom = binbuf_getnatom(b);
+			if (pos < natom && natom > 1) {
+				argv = binbuf_getvec(b);
+				if (argv->a_type != A_SYMBOL || strcmp(atom_getsymbol(argv)->s_name, "#hubpreset")) {
+					pd_error(x, "%s: malformed file", filename->s_name);
+					goto preset_hub_readpreset_fail;
+				} else {
+					// detected proper preset file format, let's continue
+					argv++;
+					// read preset we need to recall as soon as we're done reading
+					if (argv->a_type != A_FLOAT) {
+						pd_error(x, "%s: malformed file", filename->s_name);
+						goto preset_hub_readpreset_fail;
+					} else {
+						recall_preset = (int)atom_getfloat(argv);
+					}
+					argv++;
+					for (pos = 1; pos < natom; pos++, argv++) {
+						if(PH_DEBUG) fprintf(stderr,"	atom\n");
+						// SYMBOL ANALYSIS
+						if (argv->a_type == A_SYMBOL) {
+							if(PH_DEBUG) fprintf(stderr,"	data = %s\n", atom_getsymbol(argv)->s_name);
+							if (!strcmp(atom_getsymbol(argv)->s_name, "%node%")) {
+								// beginning of a new node
+								ignore_entry = 0;
+								if(PH_DEBUG) fprintf(stderr,"	new node\n");
+								loc_length = 0;
+								h_cur = H_NODE;
+							}
+							else if (!strcmp(atom_getsymbol(argv)->s_name, "%preset%") && !ignore_entry) {
+								// beginning of a new preset
+								if(PH_DEBUG) fprintf(stderr,"	new preset\n");
+								h_cur = H_PRESET;
+							}
+						}
+						// FLOAT ANALYSIS
+						else if (argv->a_type == A_FLOAT && !ignore_entry) {
+							if(PH_DEBUG) fprintf(stderr,"	data = %g\n", atom_getfloat(argv));
+							if (h_cur == H_NODE) {
+								// node location length
+								loc_length = (int)atom_getfloat(argv);
+								// reconstruct the dynamic location array
+								if (!loc_data)
+									loc_data = (int*)calloc(loc_length, sizeof(loc_data));
+								loc_data[loc_length-1] = (int)atom_getfloat(&argv[i]);
+								if(PH_DEBUG) fprintf(stderr,"	loc length = %d\n", loc_length);
+								loc_pos = 0;
+								h_cur = H_LOCATION;
+							}
+							else if (h_cur == H_LOCATION) {
+								// node location data
+								loc_data[loc_pos] = (int)atom_getfloat(argv);
+								if(PH_DEBUG) fprintf(stderr,"	loc = %d\n", loc_data[loc_pos]);
+								loc_pos++;
+								if (loc_pos == loc_length) {
+									//the location data has been collected, now check if such a node already exists
+									found_node = 0;
+
+									hd1 = NULL;
+									hd2 = NULL;
+
+									// we use hd1 to navigate and hd2 to populate
+									// first check for existing nodes and reenable them if they match location
+									hd1 = x->ph_data;
+									while (hd1) {
+										if (!preset_hub_compare_loc(hd1->phd_pn_gl_loc, hd1->phd_pn_gl_loc_length,
+											loc_data, loc_length))
+										{
+											// if this hub node data's location matches that of the node
+											if(PH_DEBUG) fprintf(stderr,"	found matching node\n");
+											found_node = 1;
+											break;
+										}
+										hd1 = hd1->phd_next;
+									}
+
+									if (!found_node) {
+										// we failed locating matching ph_data so we ignore the said data
+										// as there is no way the user will be able to predictably recreate
+										// node locations even if they wanted to
+										if(PH_DEBUG) fprintf(stderr,"	failed to find matching node\n");
+										ignore_entry = 1;
+									}
+
+									if (found_node) {
+										hd2 = hd1;
+									}
+
+									// free loc_data as we don't need it any more
+									free(loc_data);
+									loc_data = NULL;
+								}
+							}
+							else if (h_cur == H_PRESET) {
+								// preset number
+								if(PH_DEBUG) fprintf(stderr,"	preset %g\n", atom_getfloat(argv));
+								node_preset = (int)atom_getfloat(argv);
+
+								np1 = NULL;
+								np2 = NULL;
+
+								// check if the preset already exists
+								np1 = hd2->phd_npreset;
+								while (np1 && np1->np_preset != node_preset) {
+									np1 = np1->np_next;
+								}
+								if (!np1) {
+									// we need to create a new preset for the node
+									np2 = (t_node_preset *)t_getbytes(sizeof(*np2));
+									if (!hd2->phd_npreset) {
+										hd2->phd_npreset = np2;
+									} else {
+										np1 = hd2->phd_npreset;
+										while(np1->np_next) {
+											np1 = np1->np_next;
+										}
+										np1->np_next = np2;
+									}
+									np2->np_preset = node_preset;
+								} else {
+									// otherwise we simply make np2 point to np1 (which is our matching preset)
+									np2 = np1;
+									// clear old preset data
+									if (np2->np_val.l_n)
+										alist_clear(&np2->np_val);
+								}
+
+								data_count = 0;
+								argv++;
+
+								// figure out how long of variable data list follows the preset descriptor
+								while (data_count < natom && strcmp(atom_getsymbol(argv+data_count)->s_name, "%preset%") && strcmp(atom_getsymbol(argv+data_count)->s_name, "%node%")) {
+									data_count++;
+								}
+								if(PH_DEBUG) fprintf(stderr,"	found preset? %d found node? %d\n", !strcmp(atom_getsymbol(argv+data_count)->s_name, "%preset%"), !strcmp(atom_getsymbol(argv+data_count)->s_name, "%node%"));
+								if(PH_DEBUG) fprintf(stderr,"	data_count = %d starting @ %d out of %d\n", data_count, pos+1, natom);
+								alist_init(&np2->np_val);
+								alist_list(&np2->np_val, 0, data_count, argv);
+
+								if(PH_DEBUG) {
+									if ((argv)->a_type == A_SYMBOL)
+										fprintf(stderr,"	1st_element = %s\n", atom_getsymbol(argv)->s_name);
+									else if ((argv)->a_type == A_FLOAT)
+										fprintf(stderr,"	1st_element = %f\n", atom_getfloat(argv));
+									else fprintf(stderr,"	1st_element = unknown format\n");
+								}
+
+								pos = pos + data_count;
+								// we already incremented it above before figuring out
+								// how long data list is so we do one less
+								argv = argv + data_count - 1;
+								h_cur = H_PRESET_DATA;
+							}
+						}
+					}
+				}
+			} else {
+				pd_error(x, "%s: malformed/incomplete file", filename->s_name);
+				goto preset_hub_readpreset_fail;
+			}
+		}
+		result = 1;
+	}
+
+	preset_hub_readpreset_fail:
+	if (b) binbuf_free(b);
+	SETFLOAT(ap+0, (t_float)result);
+    outlet_anything(x->ph_outlet, gensym("readpreset"), 1, ap);
+	preset_hub_recall(x, recall_preset);
+}
+
+void preset_hub_writepreset(t_preset_hub *x, t_symbol *filename, float preset)
+{
+	if(PH_DEBUG) fprintf(stderr,"preset_hub_writepreset\n");
+
+	t_atom ap[1];
+	int result = 0;
+	t_atom *outv;
+	int i;
+	t_preset_hub_data *phd;
+	t_node_preset *np;
+	t_binbuf *b = binbuf_new();
+
+	if (filename == &s_) {
+		pd_error(x, "no writepreset filename given\n");
+		goto preset_hub_writepreset_fail;
+	}
+
+	phd = x->ph_data;
+	if (phd) {
+		binbuf_addv(b, "si", gensym("#hubpreset"), (int)preset);
+		while (phd) {
+			if(PH_DEBUG) fprintf(stderr,"	saving phd\n");
+			// designate a node and state whether it is active or disabled
+			// (disabled nodes are ones that have presets saved but have been deleted since--
+			// we keep these in the case of undo actions during the session that may go beyond
+			// saving something into a file)
+			binbuf_addv(b, "si", gensym("%node%"), phd->phd_pn_gl_loc_length);
+
+			// gather info about the length of the node's location and store it
+			for (i = 0; i < phd->phd_pn_gl_loc_length; i++) {
+				binbuf_addv(b,"i", (int)phd->phd_pn_gl_loc[i]);
+			}
+
+			// save preset data
+			np = phd->phd_npreset;
+			while (np) {
+				// only store preset if it matches requested preset number
+				if (np->np_val.l_n > 0 && np->np_preset == preset) {
+					binbuf_addv(b, "si", gensym("%preset%"), (int)np->np_preset);
+					for (i = 0; i < np->np_val.l_n; i++) {
+						if (np->np_val.l_vec[i].l_a.a_type == A_FLOAT)
+							binbuf_addv(b, "f", np->np_val.l_vec[i].l_a.a_w.w_float);
+						else if (np->np_val.l_vec[i].l_a.a_type == A_SYMBOL)
+							binbuf_addv(b, "s", np->np_val.l_vec[i].l_a.a_w.w_symbol);	
+					}
+				}
+				np = np->np_next;
+			}
+
+			phd = phd->phd_next;
+		}
+		binbuf_addv(b, ";");
+		result = 1;
+	}
+	if(PH_DEBUG) fprintf(stderr,"	done creating binbuf\n");
+
+    char buf[MAXPDSTRING];
+    canvas_makefilename(x->ph_canvas, filename->s_name,
+		buf, MAXPDSTRING);
+    if (binbuf_write(b, buf, "", 1)) {
+    	pd_error(x, "%s: write failed", filename->s_name);
+		result = 0;
+	}
+	if(PH_DEBUG) fprintf(stderr,"	done saving file\n");
+
+	preset_hub_writepreset_fail:
+	if (b) binbuf_free(b);
+	SETFLOAT(ap+0, (t_float)result);
+    outlet_anything(x->ph_outlet, gensym("writepreset"), 1, ap);
+}
+
 static void *preset_hub_new(t_symbol *s, int argc, t_atom *argv)
 {
 	if(PH_DEBUG) fprintf(stderr,"===preset_hub_new===\n");
@@ -1164,7 +1783,7 @@ static void *preset_hub_new(t_symbol *s, int argc, t_atom *argv)
 	// read creation arguments and substitute default for objects without optional arguments
     if (!(argc > 0 && argv[0].a_type == A_SYMBOL) || 
 		(argc > 0 && argv[0].a_type == A_SYMBOL && !strcmp(atom_getsymbol(&argv[0])->s_name, "%hidden%"))) {
-		pos--; // we subtract one position as we are essentially missing one argument	
+		pos--; // we subtract one position as we are essentially missing one argument (to counter pos++; call below that is called without checks)	
 		name = &s_;
 	}
 	else {
@@ -1185,11 +1804,18 @@ static void *preset_hub_new(t_symbol *s, int argc, t_atom *argv)
 
 	x = (t_preset_hub *)pd_new(preset_hub_class);
 	x->ph_invis = 0;
+	x->ph_extern_file = 0;
 
 	// read basic creation arguments
 	x->ph_name = name;
 	pos++;
-	if (argc > 1 && argv[1].a_type == A_FLOAT) {
+	// saving into external file
+	if (argc > pos && argv[pos].a_type == A_SYMBOL && !strcmp(atom_getsymbol(&argv[pos])->s_name, "file")) {
+		x->ph_extern_file = 1;
+		pos++;
+	}
+	// are we invisible (used by k12 mode)
+	if (argc > pos && argv[pos].a_type == A_FLOAT) {
 		x->ph_invis = (int)atom_getfloat(&argv[1]);
 		if (x->ph_invis < 0) x->ph_invis = 0;
 		/*level = x->ph_level;
@@ -1199,7 +1825,7 @@ static void *preset_hub_new(t_symbol *s, int argc, t_atom *argv)
 		}*/
 		pos++;
 	}
-	if(PH_DEBUG) fprintf(stderr,"hub name %s invis %d\n", x->ph_name->s_name, (int)x->ph_invis);
+	if(PH_DEBUG) fprintf(stderr,"hub name %s file %d invis %d\n", x->ph_name->s_name, x->ph_extern_file, x->ph_invis);
 
 	pos++; // one more time to move ahead of the %hidden% tag
 
@@ -1280,7 +1906,7 @@ static void *preset_hub_new(t_symbol *s, int argc, t_atom *argv)
 					// reconstruct the dynamic location array
 					if (!hd2->phd_pn_gl_loc)
 						hd2->phd_pn_gl_loc = (int*)calloc(hd2->phd_pn_gl_loc_length, sizeof(hd2->phd_pn_gl_loc));
-					hd2->phd_pn_gl_loc[hd2->phd_pn_gl_loc_length-1] = (int)atom_getfloat(&argv[i]);
+					hd2->phd_pn_gl_loc[hd2->phd_pn_gl_loc_length-1] = (int)atom_getfloat(&argv[i]); //WHY IS THIS HERE?
 					if(PH_DEBUG) fprintf(stderr,"	loc length = %d\n", hd2->phd_pn_gl_loc_length);
 					loc_pos = 0;
 					h_cur = H_LOCATION;
@@ -1300,9 +1926,9 @@ static void *preset_hub_new(t_symbol *s, int argc, t_atom *argv)
 					while (data_count < argc && strcmp(atom_getsymbol(&argv[data_count])->s_name, "%preset%") && strcmp(atom_getsymbol(&argv[data_count])->s_name, "%node%")) {
 						data_count++;
 					}
-					if(PH_DEBUG) fprintf(stderr,"	found preset? %d found node? %d\n", strcmp(atom_getsymbol(&argv[data_count])->s_name, "%preset%"), strcmp(atom_getsymbol(&argv[data_count])->s_name, "%node%"));
+					if(PH_DEBUG) fprintf(stderr,"	found preset? %d found node? %d\n", !strcmp(atom_getsymbol(&argv[data_count])->s_name, "%preset%"), !strcmp(atom_getsymbol(&argv[data_count])->s_name, "%node%"));
 					data_count = data_count - (i+1);
-					if(PH_DEBUG) fprintf(stderr,"	data_count = %d staring @ %d out of %d\n", data_count, i+1, argc);
+					if(PH_DEBUG) fprintf(stderr,"	data_count = %d starting @ %d out of %d\n", data_count, i+1, argc);
 					alist_init(&np2->np_val);
 					alist_list(&np2->np_val, 0, data_count, argv+(i+1));
 					i = i + data_count;
@@ -1388,6 +2014,7 @@ static void preset_hub_free(t_preset_hub* x)
 				}
 			}
 			hd2 = hd1->phd_next;
+			free(hd1->phd_pn_gl_loc);
 			freebytes(hd1, sizeof(*hd1));
 			hd1 = hd2;
 		}
@@ -1419,6 +2046,15 @@ void preset_hub_setup(void)
     class_addmethod(preset_hub_class, (t_method)preset_hub_purge,
         gensym("purge"), A_NULL, 0);
 
+    class_addmethod(preset_hub_class, (t_method)preset_hub_read,
+        gensym("read"), A_DEFSYM, 0);
+    class_addmethod(preset_hub_class, (t_method)preset_hub_write,
+        gensym("write"), A_DEFSYM, 0);
+    class_addmethod(preset_hub_class, (t_method)preset_hub_readpreset,
+        gensym("readpreset"), A_DEFSYM, A_DEFFLOAT, 0);
+    class_addmethod(preset_hub_class, (t_method)preset_hub_writepreset,
+        gensym("writepreset"), A_DEFSYM, A_DEFFLOAT, 0);
+
     class_addbang(preset_hub_class, preset_hub_bang);		// we'll use this to output current preset
 	class_setsavefn(preset_hub_class, preset_hub_save);
 }
diff --git a/pd/src/x_preset.h b/pd/src/x_preset.h
index 277b1ecbb1886f5950da2e19fd52777e98fe7db6..8f9c4f5745a39a5b80137d37208c7c8f5b654a4d 100644
--- a/pd/src/x_preset.h
+++ b/pd/src/x_preset.h
@@ -55,6 +55,10 @@ struct _preset_hub
 	int ph_invis;					// make it invisible (only for the k12 mode)
 	int ph_preset;					// last enabled preset (-1 at init time)
 
+	int ph_extern_file;				// are we reading/writing from/to a file
+									// 0 = standard mode (saving with patch) 
+									// 1 = saving into external file
+
 	t_canvas *ph_canvas;
 
 	struct _preset_hub *ph_next;	// next hub on the same canvas