1 /*****************************************************************************
2 *
3 * Copyright (c) 2008-2010, CoreCodec, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of CoreCodec, Inc. nor the
14 * names of its contributors may be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY CoreCodec, Inc. ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL CoreCodec, Inc. BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 ****************************************************************************/
29
30 #include "file.h"
31
32 //TODO: include from somewhere else or not depend on them at all
33 #ifdef CONFIG_BLOCK_RDONLY
34 #include "common.h"
35 #endif
36 //end TODO
37
DummySkip(void * p,intptr_t * Skip)38 static err_t DummySkip(void* p, intptr_t* Skip)
39 {
40 uint8_t Buf[1024];
41 size_t Readed;
42 intptr_t n = *Skip;
43 err_t Err = ERR_NONE;
44 while (n>0 && Err == ERR_NONE)
45 {
46 Err = Stream_Read(p,Buf,min(n,(intptr_t)sizeof(Buf)),&Readed);
47 n -= Readed;
48 }
49 *Skip = n;
50 return Err;
51 }
52
DummyOpen(void * UNUSED_PARAM (p),const tchar_t * Name,int UNUSED_PARAM (Flags))53 static err_t DummyOpen(void* UNUSED_PARAM(p),const tchar_t* Name, int UNUSED_PARAM(Flags))
54 {
55 if (Name)
56 return ERR_NOT_SUPPORTED;
57 return ERR_NONE;
58 }
59
DummyDuplicate(void * p,int Flags)60 static stream* DummyDuplicate(void* p,int Flags)
61 {
62 tchar_t URL[MAXPATHFULL];
63
64 if (Node_Get(p,STREAM_URL,URL,sizeof(URL)) != ERR_NONE)
65 return NULL;
66
67 return StreamOpen(p,URL,Flags);
68 }
69
DummyBlocking(void * UNUSED_PARAM (p),bool_t State)70 static err_t DummyBlocking(void* UNUSED_PARAM(p),bool_t State)
71 {
72 if (!State)
73 return ERR_NOT_SUPPORTED;
74 return ERR_NONE;
75 }
76
DummyRead(void * UNUSED_PARAM (p),void * UNUSED_PARAM (Data),size_t UNUSED_PARAM (Size),size_t * Readed)77 static err_t DummyRead(void* UNUSED_PARAM(p),void* UNUSED_PARAM(Data),size_t UNUSED_PARAM(Size),size_t* Readed)
78 {
79 if (Readed)
80 *Readed = 0;
81 return ERR_NOT_SUPPORTED;
82 }
83
DummyReadOneOrMore(void * p,void * Data,size_t Size,size_t * Readed)84 static err_t DummyReadOneOrMore(void* p,void* Data,size_t Size,size_t* Readed)
85 {
86 return Stream_Read(p,Data,Size,Readed);
87 }
88
DummySeek(void * UNUSED_PARAM (p),filepos_t UNUSED_PARAM (Pos),int UNUSED_PARAM (SeekMode))89 static filepos_t DummySeek(void* UNUSED_PARAM(p),filepos_t UNUSED_PARAM(Pos),int UNUSED_PARAM(SeekMode))
90 {
91 return INVALID_FILEPOS_T;
92 }
93
DummyWrite(void * UNUSED_PARAM (This),const void * UNUSED_PARAM (Data),size_t UNUSED_PARAM (Size),size_t * Written)94 static err_t DummyWrite(void* UNUSED_PARAM(This),const void* UNUSED_PARAM(Data),size_t UNUSED_PARAM(Size), size_t* Written)
95 {
96 if (Written)
97 *Written = 0;
98 return ERR_NOT_SUPPORTED;
99 }
100
DummyOpenDir(void * UNUSED_PARAM (p),const tchar_t * UNUSED_PARAM (URL),int UNUSED_PARAM (Flags))101 static err_t DummyOpenDir(void* UNUSED_PARAM(p),const tchar_t* UNUSED_PARAM(URL), int UNUSED_PARAM(Flags))
102 {
103 return ERR_NOT_DIRECTORY;
104 }
105
DummyEnumDir(void * UNUSED_PARAM (p),const tchar_t * UNUSED_PARAM (Exts),bool_t UNUSED_PARAM (ExtFilter),streamdir * UNUSED_PARAM (Item))106 static err_t DummyEnumDir(void* UNUSED_PARAM(p),const tchar_t* UNUSED_PARAM(Exts), bool_t UNUSED_PARAM(ExtFilter),streamdir* UNUSED_PARAM(Item))
107 {
108 return ERR_END_OF_FILE;
109 }
110
DummyReadBlock(stream * p,block * Block,size_t Ofs,size_t Size,size_t * Readed)111 static err_t DummyReadBlock(stream* p,block* Block,size_t Ofs,size_t Size,size_t* Readed)
112 {
113 #ifdef CONFIG_BLOCK_RDONLY
114 uint8_t Buf[2048]; // MAX_NETWORK_PACKET has to fit...
115 err_t Err = ERR_NONE;
116 size_t Pos = 0;
117 size_t Left;
118
119 while ((Left = (Size - Pos)) > 0)
120 {
121 if (Left > sizeof(Buf))
122 Left = sizeof(Buf);
123
124 Err = Stream_Read(p,Buf,Left,&Left);
125 if (!Left)
126 break;
127
128 WriteBlock(Block,Ofs+Pos,Buf,Left);
129 Pos += Left;
130
131 if (Left < sizeof(Buf))
132 break;
133 }
134
135 if (Readed)
136 *Readed = Pos;
137 return Err;
138 #else
139 return Stream_Read(p,(uint8_t*)Block->Ptr+Ofs,Size,Readed);
140 #endif
141 }
142
DummyWait(void * UNUSED_PARAM (p),bool_t UNUSED_PARAM (Read),streamselect * UNUSED_PARAM (Select))143 static err_t DummyWait(void* UNUSED_PARAM(p),bool_t UNUSED_PARAM(Read),streamselect* UNUSED_PARAM(Select))
144 {
145 return ERR_NOT_SUPPORTED;
146 }
147
DummyFlush(void * UNUSED_PARAM (p))148 static err_t DummyFlush(void* UNUSED_PARAM(p))
149 {
150 return ERR_NOT_SUPPORTED;
151 }
152
DummyResetReadTimeout(void * UNUSED_PARAM (p))153 static err_t DummyResetReadTimeout(void* UNUSED_PARAM(p))
154 {
155 return ERR_NOT_SUPPORTED;
156 }
157
ProcessBlocking(void * p,bool_t State)158 static err_t ProcessBlocking(void* p,bool_t State)
159 {
160 stream* Input;
161 if (Node_GET(p,STREAMPROCESS_INPUT,&Input) == ERR_NONE && Input)
162 return Stream_Blocking(Input,State);
163 return DummyBlocking(p,State);
164 }
165
ProcessWait(void * p,bool_t Read,streamselect * Select)166 static err_t ProcessWait(void* p,bool_t Read,streamselect* Select)
167 {
168 stream* Input;
169 if (Node_GET(p,STREAMPROCESS_INPUT,&Input) == ERR_NONE && Input)
170 return Stream_Wait(Input,Read,Select);
171 return ERR_NOT_SUPPORTED;
172 }
173
META_START(Streams_Class,MEDIA_CLASS)174 META_START(Streams_Class,MEDIA_CLASS)
175 META_CLASS(FLAGS,CFLAG_ABSTRACT)
176 META_END_CONTINUE(NODE_CLASS)
177
178 META_START_CONTINUE(STREAM_CLASS)
179 META_CLASS(VMT_SIZE,sizeof(stream_vmt))
180 META_CLASS(SIZE,sizeof(stream))
181 META_CLASS(FLAGS,CFLAG_ABSTRACT)
182 META_PARAM(TYPE,STREAM_KIND,TYPE_FOURCC|TFLAG_RDONLY)
183 META_PARAM(CUSTOM,STREAM_KIND,STREAM_KIND_LOCAL)
184 META_PARAM(NAME,STREAM_URL,T("URL"))
185 META_PARAM(TYPE,STREAM_URL,TYPE_STRING)
186 META_PARAM(NAME,STREAM_LENGTH,T("Length"))
187 META_PARAM(TYPE,STREAM_LENGTH,TYPE_FILEPOS)
188 META_PARAM(NAME,STREAM_FLAGS,T("Flags"))
189 META_PARAM(TYPE,STREAM_FLAGS,TYPE_INT)
190 META_PARAM(NAME,STREAM_CONTENTTYPE,T("ContentType"))
191 META_PARAM(TYPE,STREAM_CONTENTTYPE,TYPE_STRING|TFLAG_RDONLY)
192 META_PARAM(NAME,STREAM_PRAGMA_SEND,T("PragmaSet"))
193 META_PARAM(TYPE,STREAM_PRAGMA_SEND,TYPE_STRING|TFLAG_RDONLY)
194 META_PARAM(NAME,STREAM_PRAGMA_GET,T("PragmaGet"))
195 META_PARAM(TYPE,STREAM_PRAGMA_GET,TYPE_STRING|TFLAG_RDONLY)
196 META_PARAM(NAME,STREAM_META,T("Meta"))
197 META_PARAM(TYPE,STREAM_META,TYPE_META)
198 META_PARAM(NAME,STREAM_SCOUTING,T("Scouting"))
199 META_PARAM(TYPE,STREAM_SCOUTING,TYPE_BOOLEAN)
200 META_PARAM(NAME,STREAM_TIMEOUT,T("TimeOut"))
201 META_PARAM(TYPE,STREAM_TIMEOUT,TYPE_INT) // TODO: TYPE_SYSTICK
202 META_PARAM(TYPE,STREAM_DATE,TYPE_DATETIME)
203 META_PARAM(NAME,STREAM_FULL_URL,T("FullURL"))
204 META_PARAM(TYPE,STREAM_FULL_URL,TYPE_STRING)
205 META_PARAM(TYPE,STREAM_PLAYING,TYPE_BOOLEAN)
206 META_PARAM(TYPE,STREAM_ENUM_BASE,TYPE_STRING)
207 META_PARAM(TYPE,STREAM_TIME,TYPE_TICK)
208 META_PARAM(TYPE,STREAM_NATIVE_HANDLER,TYPE_PTR|TFLAG_RDONLY)
209 META_PARAM(TYPE,STREAM_CACHING,TYPE_BOOLEAN)
210 META_PARAM(TYPE,STREAM_USERNAME,TYPE_STRING)
211 META_DYNAMIC(TYPE_STRING,STREAM_USERNAME)
212 META_PARAM(TYPE,STREAM_PASSWORD,TYPE_STRING)
213 META_DYNAMIC(TYPE_STRING,STREAM_PASSWORD)
214 META_PARAM(TYPE,STREAM_PROXY_USERNAME,TYPE_STRING)
215 META_DYNAMIC(TYPE_STRING,STREAM_PROXY_USERNAME)
216 META_PARAM(TYPE,STREAM_PROXY_PASSWORD,TYPE_STRING)
217 META_DYNAMIC(TYPE_STRING,STREAM_PROXY_PASSWORD)
218 META_PARAM(TYPE,STREAM_CACHE_CLASS,TYPE_FOURCC|TFLAG_RDONLY)
219 META_VMT(TYPE_FUNC,stream_vmt,Open,DummyOpen)
220 META_VMT(TYPE_FUNC,stream_vmt,Duplicate,DummyDuplicate)
221 META_VMT(TYPE_FUNC,stream_vmt,Read,DummyRead)
222 META_VMT(TYPE_FUNC,stream_vmt,ReadOneOrMore,DummyReadOneOrMore)
223 META_VMT(TYPE_FUNC,stream_vmt,ReadBlock,DummyReadBlock)
224 META_VMT(TYPE_FUNC,stream_vmt,Write,DummyWrite)
225 META_VMT(TYPE_FUNC,stream_vmt,Seek,DummySeek)
226 META_VMT(TYPE_FUNC,stream_vmt,Blocking,DummyBlocking)
227 META_VMT(TYPE_FUNC,stream_vmt,OpenDir,DummyOpenDir)
228 META_VMT(TYPE_FUNC,stream_vmt,EnumDir,DummyEnumDir)
229 META_VMT(TYPE_FUNC,stream_vmt,Wait,DummyWait)
230 META_VMT(TYPE_FUNC,stream_vmt,Skip,DummySkip)
231 META_VMT(TYPE_FUNC,stream_vmt,Flush,DummyFlush)
232 META_VMT(TYPE_FUNC,stream_vmt,ResetReadTimeout,DummyResetReadTimeout)
233 META_END_CONTINUE(MEDIA_CLASS) // STREAMPROCESS_CLASS can have NODE_EXTS
234
235 META_START_CONTINUE(STREAMPROCESS_CLASS)
236 META_CLASS(FLAGS,CFLAG_ABSTRACT)
237 META_PARAM(NAME,STREAMPROCESS_INPUT,T("Input"))
238 META_PARAM(TYPE,STREAMPROCESS_INPUT,TYPE_NODE)
239 META_PARAM(CLASS,STREAMPROCESS_INPUT,STREAM_CLASS)
240 META_VMT(TYPE_FUNC,stream_vmt,Blocking,ProcessBlocking)
241 META_VMT(TYPE_FUNC,stream_vmt,Wait,ProcessWait)
242 META_END(STREAM_CLASS)
243
244 stream* StreamOpen(anynode *AnyNode, const tchar_t* Path, int Flags)
245 {
246 stream* File = GetStream(AnyNode,Path,Flags);
247 if (File)
248 {
249 err_t Err = Stream_Open(File,Path,Flags);
250 if (Err != ERR_NONE && Err != ERR_NEED_MORE_DATA)
251 {
252 NodeDelete((node*)File);
253 File = NULL;
254 }
255 else
256 {
257 stream* Buf;
258 if ((Flags & SFLAG_BUFFERED) && (Buf = (stream*)NodeCreate(AnyNode,BUFSTREAM_CLASS)) != NULL)
259 {
260 Node_SET(Buf,BUFSTREAM_STREAM,&File);
261 File = Buf;
262 }
263 }
264 }
265 return File;
266 }
267
StreamClose(stream * File)268 void StreamClose(stream* File)
269 {
270 NodeDelete((node*)File);
271 }
272
StreamGenExts(anynode * AnyNode,array * Exts,fourcc_t ClassFilter,const tchar_t * TypeFilter)273 bool_t StreamGenExts(anynode* AnyNode,array* Exts, fourcc_t ClassFilter, const tchar_t* TypeFilter)
274 {
275 fourcc_t* i;
276 array List;
277 ArrayInit(Exts);
278
279 if (TypeFilter && !TypeFilter[0])
280 TypeFilter = NULL;
281
282 NodeEnumClass(AnyNode,&List,ClassFilter);
283 for (i=ARRAYBEGIN(List,fourcc_t);i!=ARRAYEND(List,fourcc_t);++i)
284 {
285 const tchar_t* s = NodeStr2(AnyNode,*i,NODE_EXTS);
286 while (s && s[0])
287 {
288 size_t n;
289 for (n=0;s[n] && s[n]!=';' && s[n]!=':';++n) {}
290
291 if (!TypeFilter || (s[n]==':' && tcschr(TypeFilter,s[n+1])!=NULL))
292 {
293 while (s[n] && s[n]!=';')
294 ++n;
295
296 if (n)
297 {
298 if (!ARRAYEMPTY(*Exts))
299 ArrayAppend(Exts,T(";"),sizeof(tchar_t),64);
300 ArrayAppend(Exts,s,n*sizeof(tchar_t),64);
301 }
302 }
303
304 s = tcschr(s,';');
305 if (s) ++s;
306 }
307 }
308 ArrayClear(&List);
309
310 if (!ARRAYEMPTY(*Exts) && !ArrayAppend(Exts,T("\0"),sizeof(tchar_t),64))
311 ArrayClear(Exts);
312
313 return !ARRAYEMPTY(*Exts);
314 }
315
StreamExtType(anynode * AnyNode,fourcc_t ClassFilter,const tchar_t * Ext)316 char StreamExtType(anynode* AnyNode, fourcc_t ClassFilter, const tchar_t *Ext)
317 {
318 char Result = FTYPE_UNDEFINED;
319 tchar_t *s;
320 size_t i;
321 array List;
322 StreamGenExts(AnyNode,&List,ClassFilter,NULL);
323
324 for (s=ARRAYBEGIN(List,tchar_t);s;)
325 {
326 for (i=0;s[i] && s[i]==Ext[i];i++) {}
327
328 if (!Ext[i] && s[i] == ':')
329 {
330 Result = (char)s[i+1];
331 break;
332 }
333
334 s = tcschr(s,';');
335 if (s) ++s;
336 }
337
338 ArrayClear(&List);
339 return Result;
340 }
341
GetStream(anynode * AnyNode,const tchar_t * URL,int Flags)342 stream* GetStream(anynode *AnyNode, const tchar_t* URL, int Flags)
343 {
344 tchar_t Protocol[MAXPROTOCOL];
345 stream* Stream = NULL;
346 fourcc_t FourCC;
347
348 GetProtocol(URL,Protocol,TSIZEOF(Protocol),NULL);
349
350 FourCC = NodeEnumClassStr(AnyNode,NULL,STREAM_CLASS,NODE_PROTOCOL,Protocol);
351
352 #if defined(CONFIG_STREAM_CACHE)
353 if ((Flags & (SFLAG_NO_CACHING|SFLAG_WRONLY|SFLAG_CREATE))==0)
354 Stream = (stream*)NodeCreate(AnyNode,NodeClass_Meta(NodeContext_FindClass(AnyNode,FourCC),STREAM_CACHE_CLASS,META_PARAM_CUSTOM));
355 #endif
356
357 if (!Stream)
358 Stream = (stream*)NodeCreate(AnyNode,FourCC);
359
360 if (Stream && (Flags & SFLAG_NON_BLOCKING))
361 Stream_Blocking(Stream,0);
362
363 if (!Stream && !(Flags & SFLAG_SILENT))
364 {
365 tcsupr(Protocol);
366 NodeReportError(AnyNode,NULL,ERR_ID,ERR_PROTO_NOT_FOUND,Protocol);
367 }
368 #if defined(CONFIG_DEBUGCHECKS)
369 if (Stream)
370 tcscpy_s(Stream->URL,TSIZEOF(Stream->URL),URL);
371 #endif
372 return Stream;
373 }
374
StreamProtocolPriority(anynode * AnyNode,const tchar_t * URL)375 int StreamProtocolPriority(anynode *AnyNode, const tchar_t* URL)
376 {
377 tchar_t Protocol[MAXPROTOCOL];
378 GetProtocol(URL,Protocol,TSIZEOF(Protocol),NULL);
379 if (tcsicmp(Protocol,T("file"))==0) // override for local files
380 return PRI_MAXIMUM;
381 return NodeClass_Priority(NodeContext_FindClass(AnyNode,NodeEnumClassStr(AnyNode,NULL,STREAM_CLASS,NODE_PROTOCOL,Protocol)));
382 }
383